Open Closed Principle

ソフトウェアの構成要素は拡張に対しては開いていて、修正に対しては閉じていなければならない。 ソフトウェアの振る舞いは、既存の成果物を変更せず拡張できるようにすべき。 Before class Order: def __init__(self) -> None: self.items = [] self.quantities = [] self.prices = [] self.status = "open" def add_item(self, name: str, quantity: int, price: int) -> None: self.items.append(name) self.quantities.append(quantity) self.prices.append(price) def total_price(self) -> int: total = 0 for i in range(len(self.prices)): total += self.quantities[i] * self.prices[i] return total class PaymentProcessor: def pay_debit(self, order: Order, security_code: str) -> None: print("Processing debit payment type") print(f"Verifying security code: {security_code}") order....

February 18, 2022

Single Responsibility Principle

個々のモジュールを変更する理由がたったひとつだけになるように、ソフトウェアシステムの構造がそれを使う組織の社会的構造に大きな影響を受けるようにする。 モジュールはたったひとつのアクターに対して責務を負うべきである。 Before class Order: def __init__(self) -> None: self.items = [] self.quantities = [] self.prices = [] self.status = 'open' def add_item(self, name: str, quantity: int, price: int) -> None: self.items.append(name) self.quantities.append(quantity) self.prices.append(price) def total_price(self) -> int: total = 0 for i in range(len(self.prices)): total += self.quantities[i] * self.prices[i] return total def pay(self, payment_type: str, security_code: str) -> None: if payment_type == 'debit': print('Proccessing debit payment type') print(f'Verifying security code: {security_code}') self....

February 18, 2022

Solid

よくできたソフトウェアシステムは、クリーンなコードを書くことから始まる。レンガの出来が悪ければ、その建築は優れたものにはならない。一方、たとえよくできたレンガを使っても、ぐちゃぐちゃなもの作ってしまうことがあり得る。そこで登場するのが「SOLID」原則。 SOLID原則は、関数やデータ構造をどのようにクラスに組み込むのか、そしてクラスの相互接続をどのようにするのかといったことを教えてくれる。 SOLID原則の目的は、以下のような性質を持つ中間レベルのソフトウェア構造を作ること。 変更につよいこと 理解しやすいこと コンポーネントの基盤として、多くのソフトウェアシステムで利用できること Single Responsibility Principle 個々のモジュールを変更する理由がたったひとつだけになるように、ソフトウェアシステムの構造がそれを使う組織の社会的構造に大きな影響を受けるようにする。 モジュールはたったひとつのアクターに対して責務を負うべきである。 Opne Closed Principle ソフトウェアの構成要素は拡張に対しては開いていて、修正に対しては閉じていなければならない。 ソフトウェアの振る舞いは、既存の成果物を変更せず拡張できるようにすべき。 Liskov Substitution Principle サブクラスは、スーパークラスと置換可能でなければならない。 Interface Segregation Principle 包括的なインターフェースがあるより、いくつかの特定のインターフェースに分割すべき。 Dependency Inversion Principle ソースコードの依存関係が(具象ではなく)抽象だけを参照しているシステムが最も柔軟である。

February 18, 2022

Extract Function

動機 「関数の抽出」はきわめて頻繁に行われるリファクタリング。 いつ独立した関数として取り出すかというは「2回以上使われるコードはそれ自体を関数にすべき」のように、再利用に基づいている。 しかし、最も納得できるのは意図と実装の分離です。何をしているか調べなければわからないコードの断片があるとしたら、「何」をしているかを示す名前の関数として抽出すべきです。そうすれば関数を読み返すだけで関数の目的がすぐに伝わってきます。 手順 新たな関数を作り、その意図に沿って命名する(どうやるかではなく、何をするかによって名づける。) 抽出したいコードを、元の関数から新たな関数にコピーする。 抽出したコードを調べて、元の関数ではスコープ内だったが抽出後の関数ではスコープ外になった変数を特定する。それらをパラメータとして渡す。 全ての変数を処置したらコンパイルする。 元の関数に残った抽出前のコードを、抽出された関数への呼び出しに置き換える。 テストする。 残りのコードを見て、抽出したコードと同じまたは類似したコードを探し、「関数呼び出しによるインラインコードの置き換え」を適用し、新しい関数を呼ぶ形にできないか検討する。 Before from enum import Enum class Category(Enum): A = 1 B = 2 C = 3 def calculate_tax(category: Category, income: int) -> int: if category == Category.A: discount = 10 elif category == Category.B: discount = 5 else: discount = 0 return income * (100 - discount) // 100 After from enum import Enum class Category(Enum): A = 1 B = 2 C = 3 def calculate_tax(category: Category, income: int) -> int: discount = calc_discount(category) return income * (100 - discount) // 100 def calc_discount(category: Category) -> int: if category == Category....

February 18, 2022

Clean Code

意味のある名前 意図が明確な名前にする 意味のある対比を行う 発音可能な名前を使用する 検索可能な名前を用いる マジックナンバーは定数に メンタルマッピングを避ける 抽象レベルに適切な名前を選ぶ 広いスコープには長い名前を エンコーディングを避ける 名前で副作用を示すべき 関数 小さいこと! 1つのことを行う 内容をよく表す名前を使う 引数は少なく フラグ引数は使わず、メソッドを分割する 副作用をさける try/catchブロックの分離 DRY(Don’t Repeat Yourself) 死んだ関数は削除すべき コメント 「ダメなコードをコメントで取り繕ってはいけない。書き直すのだ」 コメントで、ダメなコードを取り繕うことはできない 自分自身をコードで説明する 意図の説明 結果に対する警告 書式化 縦方向の書式化 垂直概念分離性 垂直密度 垂直距離 水平分離性と密度 テスト 1つのテストに1つのアサート 1つのテストでは1つの概念を扱う カバレッジツールを使用する ささいなテストを省略しない 無視することを指定されたテストは、あいまいさへの問いかけである 境界条件テストには注意払う バグの周辺は徹底的にテストを 失敗パターンは何かを語る テストカバレッジパターンは何かを語る テストは高速に実行できるべき

February 17, 2022