nikkie-ftnextの日記

イベントレポートや読書メモを発信

dataclassデコレータを使ったクラスが #ミノ駆動本 でいう「データクラス」になるかは、プログラマ次第

はじめに

パチャンガ乗りたい!1 nikkieです。

最近は、主催者の1人としてミノ駆動本_読書pyを主催しています2
これまでに1,2章の回、3章の回と2回開催しました。

読書会での学びをアウトプットしていきます。
まずはPythondataclassデコレータに関して数回予定しています。

目次

まとめ:現時点のnikkieの考え

  • Python@dataclassを使うことはアンチパターン「データクラス」を作ることには ならない
  • ただし、アンチパターン「データクラス」を知らないと、気づかぬうちに作っていることもありえる
  • アンチパターン「データクラス」を知った上で積極的に@dataclassを使おう!
    • @dataclassはクラス定義の記述量を減らす、「シンタックスシュガー的なもの」と捉えています
    • 書くコード量が減るので、データを計算するロジックをがっつり書くのに注力できる!

設計入門本における「データクラス」

「データクラス」は、アンチパターンとして語られています。

ミノ駆動本

『良いコード/悪いコードで学ぶ設計入門』第1章 1.3「さまざまな悪魔を招きやすいデータクラス」にて何が悪いのか詳説されています。

データクラスは、設計が不十分なソフトウェアで頻繁に登場するクラス構造です。(p.32)

データを保持するクラス=データクラスと、データを使って計算するロジックが分散し、低凝集なコードになってしまいます。

招く悪魔はこんなにあります:

  • 重複コード(ロジックが重複)
  • 修正漏れ
  • 可読性低下
  • 生焼けオブジェクト(未初期化状態が発生しうる)
  • 不正値の混入

増田本(『現場で役立つシステム設計の原則』)

「データクラス」という語は『現場で役立つシステム設計の原則』の3章にも登場します(読書会で増田さんの本にも書いてあると教えていただきました。ありがとうございます!)

このデータクラスは、Javaの文法的には何もまちがっていません。しかし、オブジェクト指向のクラスの使い方としては、まちがっています。(Kindle の位置No.1216-1217)

このようなロジックを持たないクラスは、本来のクラスではありません。(Kindle の位置No.1218-1219)

引用した部分以外にも「なぜデータクラスが作られるのか」という考察もあるので、興味を持った方は読んでみてください。
私はスッキリしましたー

アンチパターン「データクラス」とは

  • データだけを持ち、ロジックを持たないクラス
  • オブジェクト指向のクラスの使い方として誤り

Pythonにおけるdata class

Python 3.7で標準ライブラリに加わりました。

ドキュメント

提案したPEP

@dataclassでクラスをデコレートすると

ドキュメントやPEPにある例ですが、__init__が自動で生えます(説明はドキュメントの冒頭にあります)。

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """商品明細のアイテムの最新の状態を把握するためのクラス"""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

InventoryItemクラスのインスタンスを1つ作ってみます。

$ python3.10 -i dataclass_example.py  # クラス定義をスクリプトに書いて読み込む
>>> item = InventoryItem("Awesome item", 10000)
>>> item
InventoryItem(name='Awesome item', unit_price=10000, quantity_on_hand=0)

インスタンス作成時にnameunit_priceを指定し、quantity_on_handはデフォルト値を指定しました。

__init__のほか__repr__も自動で生えるので、対話モードでの表示が分かりやすいですね。

@dataclassでデコレートしたクラスをアンチパターン「データクラス」にしないために

上記例を見ていただくと、total_costメソッドが定義されています。
これは合計コストを計算するロジックであり、このInventoryItemクラスはデータクラスではないですね。
「この例はよくできているなー」と思っていて、@dataclassを使ったクラスにロジックも持たせているわけです。

アンチパターン「データクラス」は、データだけを持ち、ロジックを持たないクラスでした。
@dataclassを使い、さらにそのクラスにロジックも実装することで、アンチパターン「データクラス」は回避できます!

@dataclassを使うメリット

使わないときと比べてコードの記述量が減る、これに尽きます。

インスタンスが持つ属性の名前と型を書くだけで、実行時に自動的に__init__を持ちます。
__init__を持つので、作ったインスタンスが生焼けになることもありません。

@dataclassを使ってミノ駆動本で紹介された設計を実装できると考えていますが、それは別の記事でアウトプットすることにします。

終わりに

この記事は以下の返信へのアンサー記事でもあります。

ミノ駆動本_読書pyでは「Pythonで実装するならこうかな」というアウトプットにフィードバックをいただき、自分の理解も深まってなかなか刺激的です。
気になるところを読んでいく形ですので、興味がある箇所の回がありましたら、お気軽にご参加ください。
次回は条件分岐の中の6.2(switch文の重複)です!



  1. であいもん最終話、この一果ちゃんかわいかったー。和さんの妄想ですが

  2. ミノ駆動本=『良いコード/悪いコードで学ぶ設計入門』。これまでにこんな記事も書いています: