見出し画像

「レガシーコードからの脱却」を読んだのでまとめ③(プラクティス5 - CLEANコードを作る~最後まで)

5. CLEANコードを作る

計測可能なコード品質を保つためのアプローチとして、「CLEAN」コードを紹介している。

C: Cohesive / L: Loosely-coupled / E: Encapsulated / A: Assertive / N: Nonredundant

※ Loosely-coupledはそのまま弱連結の意。Encapsulatedはカプセル化と訳されているが、正確にはカプセル化された状態を指すはず。EとNは接頭辞で覚えにくいので、意識して覚える必要あり。

神オブジェクトのジョークは面白かった。そのあとの「凝集性の低いメソッドがある場合に無関係に結合する必要が出てくる、だから神APIは良くない」というのも納得した。

カプセル化というのは、単体で完結されている=インターフェースと実装が切り離されていることを指している。これを実現するために、インターフェースを抽象化する必要があるとも言っている。

6. まずテストを書く

まず、テスト駆動開発は死んだのか、という導入から始まる。もちろんこの記事のことだろう。この章では、極めて冷静に、これは「TDD導入に失敗した奴らの負け惜しみだ」ということを伝えようとしている。

そもそもテストには複数あり、「受け入れテスト=顧客テスト」、「ユニットテスト」、「QAテスト(結合テストなど)」に分類できると言っている。

ここで共有すべき概念として、「ユニットはふるまいの単位である」ということを長々と語っている。つまり、その後(p173)にあるように、ユニットテストはインターフェースに対して行われるべきだとも言っている。

また、リファクタリングについても、マーチンファウラーの「ふるまいを保ったまま内部構造を改善すること」と定義している。

これは単純だけど重要なので、初学者が含まれる場合は言語化してチームで共有しておくべき概念と定義だと思う。

ユニットテストはインターフェースに対して行われるべきということは、外部の実装(例えばSaaS、PaaSのようなもの)の返却をテストすることは正しくない。個別具体的にいえば、Firebaseのupdateの返り値を検証することは、ユニットテストとして正しくない、ということだ。

7. テストで振る舞いを明示する

筆者はテストファースト開発(多分TDDのこと)には3つのフェーズがあるとしており、レッド・グリーン・リファクタと呼んでいる。まずはStubを作って失敗する状態(レッド)をわざと作る。次にテストパスのための最小限のコードを書く(グリーン)。その後にコードを向上させていく(リファクタ)。必ず最初に失敗するコードを書くことで、TDDのリズムを作っていくことが目的だという。

7-b. TDDの具体的な進め方

ここから具体的なコードの手順が説明される。

① まずは嘘のテストコードを作成する。最初に書いたこのテストコードはまだ作成すらしていないクラスやメソッドを呼び出そうとしているので、コンパイラを通すとエラーが出ている。テストメソッドの命名は、何をするかを明確にするために冗長なくらい長い名前をつける。

② 次に、コンパイラを通すためだけのStubを作成する(クラスもメソッドも)。返り値は型だけ合わせて、値は一致しない状態(レッド)にしておく。

最後に値を一致させるロジックを追加して、成功の状態(グリーン)にしたら、テストコードの実装が完成する。

この後に、制約(最小値・最大値)を付すテストコードが加えられる。これを含め、テストコードは仕様を表すものだ、と言っている。さらにTDDは開発者にとってのセーフティネットになり得るとも言っている。

8.  設計は最後に行う

※ わかりにくいが、これは実装の設計を最後に行うという意味で、抽象化したストーリーの設計を最後に行うと言っているわけではない。

実務的・技術的な側面で、コードの変更可能性を損なうものとして、カプセル化の欠如継承の過度な利用具体的すぎる実装インラインコードなどをあげている。

また、持続可能な開発に必要な5つのプラクティスとして、死んだコードを消す名前を更新する判断を集約する抽象化クラスを整頓する、をあげている。

コードは書かれることよりも読まれることの方が多い。なので、それを意識するべきだとも良いている。コメントについては書いても良いが、コードがやっていることを説明してはいけない(それはコードがやる)。コードがなぜそうなっているのかを説明する

また、条件分岐は理解コストを増大させるので、なるべく使うべきでない、それならポリモフィズムで分岐タイミングをオブジェクト生成時に持ってきた方が良いと言っている。

9. レガシーコードをリファクタリングする

なぜリファクタリングをするのか、と聞かれたら、コスト削減のためと答えるべきという。どんなコストを削減するかといえば、

1. 後からコードを理解するコスト

2. 新しい機能を追加するコスト

3. ユニットテストの追加コスト、さらなるリファクタリングのコスト

3については、金銭的負債でいう利子に似ている、と言っている。利子を払うのが面倒になり、それが指数関数的に増えて払えなくなっていく。

また、リファクタリングのテクニックとして、ピンニングテスト、DI、ストラングラーパターン、抽象化によるブランチを挙げている。

ピンニングテストは粒度が粗いが、これを担保することでDIなどを行う上でのセーフティネットになる。

稼働しているシステムを変更するときには、ストラングラーパターンを使う。古いサービスをラッピングして新しいサービスを作り、置き換えていく新しいクライアントからは古いサービスのインターフェースを呼び出さないようにする

抽象化によるブランチはMartin Fowlerが名付けた物で、インターフェースを抽出し、実装を新しく書く、というもの。

最後に、筆者の人生を変えた言葉として、オープンクローズドの原則の説明をしている。具体的にいうと、新しく機能を追加するときには、既存コードの変更を最小限にし、新しくコードを書く、というものだ。

最終章(レガシーコードからの学び)と、本の感想

最後の章はほとんどコラムだが、要点は「なぜやるか」が最も重要だという話だった。この本のいたるところでウォーターフォールの問題点データから見たTDDの優位性などが散りばめられているのは、開発者がなぜTDDを行うべきかということを伝えるためだったと解釈できる。

この本を読んでみて、僕は元々XPをReact Nativeのアプリ開発チームで実践していたが、TDDの部分をPDD=プロトタイプ駆動開発に置き換えていた。なぜならReact Nativeであれば、プロトタイプを立ち上げて振る舞いを検証するコストが圧倒的に低いので、それが可能だと考えたからだ。

なのでTDD以外の部分はほとんど知っている内容だった。一方でTDDに関しては、より精密なロジックの実装の部分でTDDを共存させることも必要だと感じた。

この記事が気に入ったらサポートをしてみませんか?