プログラムのより良い設計を支える中心的概念としてコード品質特性があります。これに点数付けした場合に、悪い点をとるためにはどうすればいいかについて書きました。
以下内容のベースはJavaです
そもそもコード品質特性とは
ここでは、以下をコード品質特性としてとりあげます
- 凝集度(cohesion)
- 疎結合(loose coupling)
- 重複無し(zero duplication)
- カプセル化(encapsulation)
- テスト容易性(testability)
- 可読性(readablity)
各々の項目で悪い点を取るためにはどうすればいいか以下の各項目を参考にしてください
凝集度(cohesion)
基本的に、凝集度は高い方が良いと言われます。凝集度を低くするためには、クラスやパッケージの責務を増やせばよいです。そのためには以下に従うと良いでしょう。
- 1クラスを大きくする
- 1クラスが大きければ、基本的にはそのクラスが持つ責務は増えます
- 大量のインターフェースを実装させたクラスを作る
- 実装したインターフェースが多いということは、それだけ役割があるということです。したがって責務も多くなります
- クラス名を極端に汎用的にする
- 汎用的過ぎると色々なメソッドが配置できます。結果的に責務が増えます
- パッと見たときのクラス名の意味が分からないようにする
- クラス名の意味がよく分からないと、クラスが整理されることは無く、機能追加時に何となくメソッドが増えたりします
- 1パッケージに大量のクラスを配置する
- パッケージで見たときの責務がたくさんある状態と言えるでしょう
疎結合(loose coupling)
基本的に、結合度が低い(疎結合である)ことが望ましいと言われます。結合度を高くするにはどうすればいいかは以下項目を参考にしてください
- 実装の継承をする
- 実装の継承は親クラスと子クラスの結合度を高めてくれる道具の一つです
- インターフェースを作らず実装クラスだけ作る
- 実装クラス同士の依存関係は結合度を高めてくれます
- DIは用いない
- Dependency Injection(DI)を使ってしまうと結合度が下がることがほとんどです。基本的に使わないようにしましょう。
- シングルトンパターンを多用する
- シングルトンパターンは具体的なオブジェクトを必要とします。結合度を高くするには良い手段です。
- Observerパターンを用いない
- Observerパターンはクラス間の関係性を緩くすることがあります。Observerを介さずに、直接必要な機能を呼び出しましょう。
- パッケージ間で相互依存をする
- 一方向に比べて双方向の依存は依存関係をとても複雑にします。
- 必要な機能を提供するクラスのインスタンスを生成するためにコンストラクタを呼び出す
- new によりインスタンス生成するのは普通にやることですが、コンストラクタを呼び出すと必ず具象クラスが登場することになります。コンストラクタ呼び出しによりクラス間の結合度は高まります。
重複無し(zero duplication)
いわゆるDRY原則に反することをすればおのずと悪い点数となるでしょう。
- コピペを繰り返す
- コピペを繰り返せば重複が大量に発生します
- 汎用的な処理を見出さない
- ほとんどの業務アプリケーションにおいて、ビジネスロジックと呼べる核となる部分は大きくはありません。核となる部分以外は基本的に汎用的な物として切り出せます。が、あえてそれをしなければ各機能の中に重複した処理がたくさん生まれます。
- ライブラリを使用しない
- 世の中にある汎用的なライブラリを使用しなければ、基本的に車輪の再発明をし続けることになります。重複コードがきっと増えます
カプセル化(encapsulation)
スコープを広げればカプセル化は破られるでしょう。また、できるだけ内部の実装に依存されるようにすればさらに悪い点が取れるでしょう。
- スコープを大きく広げる
- どんなクラスもpublicにしましょう。メソッドやメンバのスコープも広げましょう
- 実装の継承をする
- 実装の継承をすると基本的にカプセル化が破られます。
- ポリモーフィズムを用いない
- ポリモーフィズムを使用することは、実装を隠蔽(カプセル化)することにつながります。
テスト容易性(testability)
ユニットテストをするためのセットアップ等を困難にすれば悪い点数となるでしょう。
そもそも意識的にユニットテストがし易いように作らなければ良い点は取りにくいです。
- 外部環境に依存させる
- 外部環境(ファイルやミドルなど)に依存したクラスを作ると、インスタンス化が困難になります。インスタンス化できなければ簡単にはユニットテストは書けません。
- クラス間にインターフェースを介在させない
- インターフェースを介在させるとテストし易くなります。できるだけ実装クラス同士で関係性を持つようにしましょう
- Injectionを使用しない
- コンストラクタInjection等で対象クラスに必要な機能を外からセットできるようにするとテストし易くなってしまいます。必要な機能は対象クラスの中で自ら取得するようにしましょう
- メソッドの戻り値をできるだけvoidにする
- 戻り値が無ければ対象メソッドの振る舞いが予定通りなのかどうか判定しにくくなります
- クラスにたくさんのインスタンス変数を持たせる
- 対象クラスのセットアップがかなり大変な状態になり、テストがしにくくなります。
可読性(readablity)
長いコードや深いコードを書きましょう。また命名を適当にやっていればソースコードは読みにくくなります。
- 「ソースコードを汚くするには」に記載した内容を実施する
- 基本的に以下のようなコードは可読性が低いと思えばよいです
- 長い
- ネストが深い
- 条件分岐が多い
- 名前が分かりにくい
- マジックナンバーを使用する
- マジックナンバーを使用するとその値の意図が伝わらなくなります。
- 手続き的な書き方をする
- 何がしたいか?ではなく、どうやって実現するのか?をソースコードに書き表せば可読性は下がるでしょう。
補足
コード品質特性を悪化させる方法をいくつか書きましたが、ほとんどの人が、通常はこのようなことを意識的にやることはありません。
しかし、当初品質が高く保たれていたプログラムでも、機能追加をしてリリースを繰り返しているといつの間にか品質特性が悪化しています。
(機能追加によりプログラム改修をする際には、)我々は無意識のうちに品質の悪化につながるようなプログラムの書き方をしていることになります。
コードの品質を保つことについては、プログラムの新規作成時よりも機能追加する際により注意が必要ということになります。
プログラム改修時にメソッドの処理内に新しくifの条件分岐を足す場合も、本当にその分岐を配置していいのか?と自問すべきでしょう。