Change Function Declaration

関数宣言の変更 動機 関数宣言はソフトウェアシステムにおける継ぎ目となる。多くの部分が継ぎ目に依存し、良い継ぎ目があればシステムに新たな部品を追加するのは簡単。悪い継ぎ目は常に問題の発生源になる。 継ぎ目の要素で最も重要なのは関数名です。もし間違った名前の関数を見たら、少しでも名前がわかり次第、断固として変更していかなければなりません。 関数の名前を改善していくための良い方法は、関数の目的をコメントとして書くこと。そのコメントを名前に変更する。 手順 変更内容を吟味して、宣言と呼び出しとを一度で簡単に変更できるかを検討する。もしできるなら簡易な手順を実行する。 そうでなければ移行手順を選択して呼び出し側より漸進的に変更していく。 この手順が大きな意味を持つのは、呼び出し箇所が多い場合や(公開されたAPIのように)呼び出し箇所が手の届かないところにある場合、関数がポリモーフィックなメソッドである場合、関数宣言に複雑な変更を加える場合です。 簡易な手順 パラメータを削除する場合、それが関数内部で参照されていないことを確認する。 関数宣言を望ましいものに変更する。 古い関数宣言へのすべての参照を探し、新しいものに変更する。 テスト 名前の変更とパラメータの追加の両方がある場合には、それぞれ別のステップで実施する。 移行手順 必要なら関数の本体をリファクタリングして、移行の抽出のステップを実施しやすくしておく。 関数本体に関数の抽出を施して、新たな関数を作る。 新たな関数の名前を古いものと同じにする予定なら、新たな関数に判別しやすい名前を一時的に付けておく。 抽出した関数が追加のパラメータを必要とする場合、簡易な手順により追加を行う。 古い関数に「関数のインライン化」を施す。 一時的な名前を使った場合、「関数宣言の変更」を再び実施し、元の名前に戻す。 テスト

February 20, 2022

Inline Function

関数のインライン化 動機 意図がわかるように命名された短い関数を使うことはコードを読みやすくする。しかし、関数の本体がその名前と同じくらいわかりやすいことがある。そうしたときには関数を取り除く。 「関数のインライン化」は、うまく分割できない関数群があるときにも使われる。こうしたときは、それらを1つの大きな関数にインライン化してから、望ましい形の関数群として再抽出すればよい。 通常、「関数のインライン化」を使うのは、間接化しすぎた結果、どの関数も別の関数へ委譲しているだけにしか見えず、委譲に次ぐ委譲の途中で迷っている状態。 手順 関数がポリモーフィックなメソッドでないことを確認する。 クラスのメソッドで、サブクラスでオーバーライドされている場合はインライン化できない。 この関数の呼び出し元をすべて見つける。 関数の各呼び出し元をすべて見つける。 1つ置き換えるごとにですとする。 すべてのインライン化を一度にする必要はない。一部のインライン化がやりにくいようなら、機会を持って徐々に進めてもよい。 関数の定義を取り除く Before def say_state(self): print_state(self) def print_state(self): print(f"I'm going {self.speed} kph!") After def say_state(self): print("I'm going {} kph!".format(self.speed))

February 20, 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

Code Smells

Bloaters 長い関数(Long Method) オブジェクトそのものの受け渡し、関数の抽出、コマンドによる関数の置き換え、問い合わせによる一時変数の置き換え、条件記述の分解、パラメータオブジェクトの導入、ポリモーフィズムによる条件記述の置き換え、ループの分離 巨大なクラス(Large Class) クラスの抽出、サブクスによるタイプコードの置き換え、スーパークラスの抽出 データの群れ(Data Clumps) オブジェクトそのものの受け渡し、クラスの抽出、パラメータオブジェクトの導入 長いパラメータリスト(Long Parameter List) オブジェクトそのものの受け渡し、関数群のクラスへの集約、問い合わせによるパラメータの置き換え、パラメータオブジェクトの導入、フラグパラメータの削除 基本データ型への執着(Primitive Obsession) オブジェクトによるプリミティブの置き換え、クラスの抽出、サブクラスによるタイプコードの置き換え、パラメータオブジェクトの導入、ポリモーフィズムによる条件記述の置き換え Tool Abusers Switch Statements 相続拒否(Refused Bequest) 委譲によるサブクラスの置き換え、委譲によるスーパークラスの置き換え、フィールドの押し下げ、メソッドの押し下げ クラスのインターフェース不一致(Alternative Classes w/ Different Interfaces) 関数宣言の変更、関数の移動、スーパークラスの抽出 一時的属性(Temporary Field) 関数の移動、クラスの抽出、特殊ケースの導入 Change Preventers Divergent Change 変更の分散(Shotgun Surgery) 関数群のクラスへの集約、関数群の変換への集約、関数の移動、関数のインライン化、クラスのインライン化、フィールドの移動、フェーズの分離 Parallel Inheritance Hierarchies Dispensables Lazy Class 疑わしき一般化(Speculative Generality) 関数宣言の変更、関数のインライン化、クラス階層の平坦化、クラスのインライン化、デッドコードの削除 データクラス(Data Class) 重複したコード(Duplicated Code) 関数の抽出、ステートメントのスライド、メソッドの引き上げ Couplers 特性の横恋慕(Feature Envy) 関数の移動、関数の抽出 Inappropriate Intimacy メッセージの連鎖(Message Chains)...

February 17, 2022