Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

その他の原則

デメテルの法則(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)

呼び出し元がオブジェクトの内部構造(walletmoney の存在)を知ってしまうと、内部構造を変えるたびに呼び出し元も 壊れる。これは 結合度 でいう内容結合・スタンプ結合に近い状態であり、デメテルの法則は それを避けるための具体的なコーディング規約と位置づけられる。

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的に契約を明示し、信頼できない外部境界では防御的に検証する」という 使い分けが実践的。