はじめに
最近は、主催者の1人としてミノ駆動本_読書pyを主催しています2。
これまでに1,2章の回、3章の回と2回開催しました。
読書会での学びをアウトプットしていきます。
まずはPythonのdataclass
デコレータに関して数回予定しています。
目次
- はじめに
- 目次
- まとめ:現時点のnikkieの考え
- 設計入門本における「データクラス」
- Pythonにおけるdata class
- @dataclassでデコレートしたクラスをアンチパターン「データクラス」にしないために
- 終わりに
まとめ:現時点の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)
インスタンス作成時にname
とunit_price
を指定し、quantity_on_hand
はデフォルト値を指定しました。
__init__
のほか__repr__
も自動で生えるので、対話モードでの表示が分かりやすいですね。
@dataclass
でデコレートしたクラスをアンチパターン「データクラス」にしないために
上記例を見ていただくと、total_cost
メソッドが定義されています。
これは合計コストを計算するロジックであり、このInventoryItem
クラスはデータクラスではないですね。
「この例はよくできているなー」と思っていて、@dataclass
を使ったクラスにロジックも持たせているわけです。
アンチパターン「データクラス」は、データだけを持ち、ロジックを持たないクラスでした。
@dataclass
を使い、さらにそのクラスにロジックも実装することで、アンチパターン「データクラス」は回避できます!
@dataclass
を使うメリット
使わないときと比べてコードの記述量が減る、これに尽きます。
インスタンスが持つ属性の名前と型を書くだけで、実行時に自動的に__init__
を持ちます。
__init__
を持つので、作ったインスタンスが生焼けになることもありません。
@dataclass
を使ってミノ駆動本で紹介された設計を実装できると考えていますが、それは別の記事でアウトプットすることにします。
終わりに
この記事は以下の返信へのアンサー記事でもあります。
dataclassはプリミティブな型使うよりはマシですが、そもそもこの本で批判されてるデータクラスになるんで、微妙なんですよね…
— やまいも (@yappy0625) 2022年5月2日
ミノ駆動本_読書pyでは「Pythonで実装するならこうかな」というアウトプットにフィードバックをいただき、自分の理解も深まってなかなか刺激的です。
気になるところを読んでいく形ですので、興味がある箇所の回がありましたら、お気軽にご参加ください。
次回は条件分岐の中の6.2(switch文の重複)です!