コードやモジュールの設計判断を助ける、経験則としての原則群。多くは「変更しやすさ」を守るための知恵であり、その根拠は 結合度と凝集度 にある。厳密な理論ではなく指針なので、状況に応じて適用する。
思想・心がけ¶
「自分はバカで、とんでもないミスをするものだ」と考える¶
ミスをする前提だからこそ、大きな問題が起きる前に早く気づくためにテストを書く
半年後の自分は他人¶
自分が読むコードでも他人が読めるように書く
原則¶
KISS: Keep It Simple, Stupid¶
「設計や実装は、できる限り単純で分かりやすく保て」 という原則。
複雑な実装よりも、単純で直感的なもののほうが理解しやすく、ミスも少ない。
YAGNI:You Aren’t Gonna Need It¶
「それはきっと必要にならない」。必要になるまで機能を実装するな、という原則。
将来使うかもしれない機能を先回りして作るのは避けるべき。実際に必要になってから作る。
機能を追加すると、コードは複雑さを増し、バグのリスクが上がる。
不要な機能は作らない。さらに言うと、かつて必要だった機能も不要になった場合は消す。
DRY: Don’t Repeat Yourself¶
「同じコードを繰り返して書くな」という原則。
定数リテラルが直接埋め込まれていることも含む。定数は共通化するべき。
同じ処理は適切にループ処理を書いたり、抽象化して共通の関数に入れたりするべき。
(処理の内容を日本語に置き換えただけのようなコメントもやめるべき)
PIE: Program Intently and Expressively¶
「意図を表現してプログラミングせよ」という意味。
最も詳細なソフトウェアの設計書はコードになるので、読み手に意図が伝わるように書く。
関数名、変数名はわかりやすくする。
また、コメントを書く場合は「なぜそうしたのか」を書く。(Howはコード、Whyはコメント)
SLAP: Single Level of Abstraction Principle¶
「抽象化レベルの統一」。コードを書くとき、機能の複雑さや抽象化のレベルに応じて多層に分離し、それぞれの層における抽象化のレベルは揃えるべき、というもの。
def 高水準():
中水準1()
中水準2()
def 中水準1():
低水準1()
低水準2()
def 低水準1():
...
...レベルが揃った関数へとコードが分割されていると、
要約性をもつ:関数の一覧が目次のようになる
閲覧性が良くなる:分割された関数は小さなコードの塊になり、読みやすくなる
といったメリットがあるため。
関数を構造化すると、各関数は自身より1段階低いレベルの関数を呼び出す処理が中心となる。このような他の関数を呼び出すコードで構成された関数を 複合関数 (composed method) という。
複合関数は極力小さくするのがよく、1つの複合関数で呼び出す関数たちの抽象レベルも揃えるのがよい。
モジュールも同様に同様の抽象レベルのものを同階層に揃えるようにすると要約性と閲覧性が改善されて読みやすくなる。
例
# 悪い例(複雑な1つのメソッド)
class Calculator:
def calculate_circle_area(self, radius):
# 全て1つのメソッドに詰め込んだ例
if radius <= 0:
raise ValueError("半径は正の数である必要があります")
pi = 3.14159
area = pi * radius * radius
return round(area, 2)簡単のため行数が短い最低限のコードにしているが、これが仮に50行や100行となっていたらかなり複雑。
# 良い例(Composed Method)
class Calculator:
def calculate_circle_area(self, radius):
"""円の面積を計算(メインの処理フロー)"""
self._validate_radius(radius)
return self._compute_area(radius)
def _validate_radius(self, radius):
"""半径の検証"""
if radius <= 0:
raise ValueError("半径は正の数である必要があります")
def _compute_area(self, radius):
"""面積の計算"""
pi = 3.14159
area = pi * radius * radius
return round(area, 2)関心の分離¶
関心(ソフトウェアの機能や目的)を独立したモジュールとして、他のコードから分離すること。
例えばMVCはModel, View, Controllerに若手いる
アスペクト指向プログラミング(AOP: Aspect-Oriented Programming) は、 横断的関心事(cross-cutting concerns) を分離するためのプログラミングパラダイム
横断的関心事は複数のモジュールに横串で関係するもので、例えば
ログ記録
認証・認可
例外処理
トランザクション管理
など。
Pythonだとデコレーターを使って実装できる
def logging_decorator(func):
def wrapper(*args, **kwargs):
print(f"{func.__name__} 開始")
result = func(*args, **kwargs)
print(f"{func.__name__} 終了")
return result
return wrapper
@logging_decorator
def process_order():
print("注文処理中...")DRY, WET, AHA¶
「コードの重複を避けよ」という原則(DRY: Don’t Repeat Yourself)は有名だが、字義通りにDRYにすることのデメリットも指摘されている。
多少のコードの重複を受け入れたほうが結果として可読性・保守性が上がることもある。
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system
(あらゆる知識は、システム内で単一の、明確で、権威ある表現を持たなければならない。)
You can ask yourself “Haven’t I written this before?” two times, but never three.
(「これ、前にも書いたっけ?」と自分自身に尋ねることは 2 回まで、3 回はないようにする)
Stop trying to be so DRY, instead Write Everything Twice (WET) - DEV Community
prefer duplication over the wrong abstraction