デメテルの法則(Law of Demeter)¶
「最小知識の原則」とも呼ばれる。あるオブジェクトは、直接の友人(自分のフィールド・引数・自分が生成したオブジェクト) のメソッドだけを呼ぶべきで、友人の友人のメソッドを連鎖して呼ぶべきではない、という原則。
# 違反:customer → wallet → money と内部構造をたどっている(Train Wreck)
def charge(customer, amount):
customer.get_wallet().get_money().subtract(amount)
# 準拠:customerに「請求してほしい」と伝えるだけで、内部構造を知らなくてよい
def charge(customer, amount):
customer.charge(amount)呼び出し元がオブジェクトの内部構造(wallet や money の存在)を知ってしまうと、内部構造を変えるたびに呼び出し元も
壊れる。これは 結合度 でいう内容結合・スタンプ結合に近い状態であり、デメテルの法則は
それを避けるための具体的なコーディング規約と位置づけられる。
CQS(Command Query Separation)¶
「問い合わせ(Query)」と「コマンド(Command)」を分けよという原則。Bertrand Meyer が提唱した。
問い合わせ(Query) :値を返すが、状態を変更しない(副作用がない、読み込みのみ)
コマンド(Command) :状態を変更するが、値は返さない(書き込みのみ)
# 違反:値を返しつつ在庫も減らしている(呼び出し側が副作用に気づきにくい)
def pop_and_get_next_item(queue):
item = queue.pop(0)
return item
# 準拠:問い合わせとコマンドを分離
def peek_next_item(queue):
return queue[0]
def remove_next_item(queue):
queue.pop(0)CQSを システム全体 の書き込み・読み取りモデルの分離にまで拡張した設計が CQRS。 CQSが1メソッド単位のミクロな規律であるのに対し、CQRSはモデル・データストアのレベルにまで分離を広げたマクロな適用といえる。
最小驚きの原則(Principle of Least Astonishment)¶
インターフェースや関数の振る舞いは、利用者の直感的な予想と一致しているべき、という原則。名前から推測される動作と 実際の動作にギャップがあると、バグの温床になったり誤用を招いたりする。
# 驚きがある例:get_ という名前なのに副作用(保存)を持つ
def get_user(user_id):
user = db.find(user_id)
log_access(user_id) # ログを書き込むだけならまだしも…
user.last_accessed = now()
db.save(user) # 「取得」のはずが実は書き込みも行っている
return user「読み取り専用に見える関数名なのに書き込みをする」は上述のCQS違反の典型例でもあり、最小驚きの原則とCQSは 同じ問題を別の角度から捉えたものといえる。
契約による設計・防御的プログラミング¶
契約による設計(Design by Contract, DbC):関数やメソッドが満たすべき「事前条件(呼び出す側の責任)」 「事後条件(実装する側の責任)」「不変条件」を契約として明示する考え方。Bertrand Meyer が Eiffel 言語で提唱した。
防御的プログラミング(Defensive Programming):呼び出し側が契約を破る可能性を前提に、関数の内部で入力を 検証し続ける考え方。
両者はしばしば対立する考え方として語られる。DbCは「契約を守るのは呼び出し側の責任。契約違反はバグなので早期に 例外で落とす」という立場を取り、際限のない防御的チェックによるコードの肥大化を避けようとする。
def withdraw(self, amount):
assert amount > 0, "事前条件違反: amount は正の数でなければならない" # 契約の明示
assert amount <= self.balance, "事前条件違反: 残高不足"
self.balance -= amount
assert self.balance >= 0 # 事後条件・不変条件の確認一方で、外部からの入力(ユーザー入力、外部APIのレスポンスなど、契約を強制できない境界)に対しては防御的な 検証が必要になる。「信頼できる内部コード間ではDbC的に契約を明示し、信頼できない外部境界では防御的に検証する」という 使い分けが実践的。