はじめに
下記の本を読んで学んだことをざっくり記述していきます。
良いコード/悪いコードで学ぶ設計入門
スコープを狭くすることや可読性・保守性を意識するのは心掛けていたので
前から興味がありちょっと前にあったAmazon Kindleセールみたいなので買いました。
目的
各章で学んだことを実施することで下記のようなことができるのではないかと思います。
- 可読性を上げる
- コード量を減らせる
- 影響範囲を狭くできる
- 保守性があがる
- バグを減らす
- 問題があればコンパイル時点でエラーを確認できる
※メソッドが呼ばれて初めてバグが発覚するというのを減らす
- 問題があればコンパイル時点でエラーを確認できる
1章
- 変数名などは他人が見てもわかるようにしよう
- ネストしまくるのはやめよう
2章
- 用途が違うものを同じ変数にいれないようにしよう
- 機能ごとにメソッドを分けよう
- 関連機能は同じクラスにまとめよう
3章
- クラス単体で動くようにしよう(外部依存を減らす)
- インスタンス変数はコンストラクタで初期設定するなどしてヌルぽを回避
- コンストラクタに不正な値が渡されたらスローしてインスタンス化させない
- 基本finalで再代入を禁止しよう
- インスタンス変数がfinalの場合は宣言時とコンストラクタ内でのみ代入可能
- インスタンス変数がfinalで値を更新したい場合は新たにインスタンス化
- インスタンスを生成しすぎてパフォーマンスに影響があるなら可変にする
- 引数は値の渡し間違い(同じ型だけど別の変数)を防ぐために、クラス自体を渡そう
class Calculation {
final int value;
public Calculation(final int value) {
this.value = value;
}
Calculation add(final Calculation calculation) {
return new Calculation(this.value + calculation.value);
}
}
final Calculation calculation = new Calculation(1);
System.out.println(calculation.value); // 1
final Calculation addCalculation = calculation.add(new Calculation(2));
System.out.println(addCalculation.value); // 3
- 不要なメソッドは作らないようにしよう
- 設計パターンについて
- 完全コンストラクタ:ヌルぽなどの不正状態を防ぐ
- 値オブジェクト:高凝集にする
- ストラテジ:ロジックを単純化する
- ポリシー:条件分岐を単純化したり、カスタマイズできるようにする
- ファーストクラスコレクション:コレクションに関するロジックを高凝集にする
- スプラウトクラス:既存のロジックを変更せずに機能を追加する
4章
- final使おう
5章
- インスタンス化しよう
- staticを使えばインスタンス変数は使えなくなります。
クラスで扱いたいデータと処理が乖離することになり低凝集になります。
上記のCalculationクラスだとvalueを他のクラスに任せる感じになります。
- staticを使えばインスタンス変数は使えなくなります。
- メソッドにstaticを付けるのはインスタンスに依存しないものにしよう
- ファクトリメソッドを使おう
- privateコンストラクタにして、staticメソッドによりコンストラクタを実行
package org.example;
public class Point {
private static final int MIN_POINT = 0;
private static final int STANDARD_POINT = 1000;
private static final int PREMIUM_POINT = 10000;
final int value;
private Point(final int point) {
if(point < MIN_POINT) {
throw new IllegalArgumentException("ポイントが不正です。");
}
value = point;
}
static Point standardMemberShip() {
return new Point(STANDARD_POINT);
}
static Point premiumtMemberShip() {
return new Point(PREMIUM_POINT);
}
}
- 共通処理に機能構わず記述するのはやめよう
- 引数を戻り値として使用するのはやめよう
- vb.netのByRef(参照渡し)
- リストを外部から渡す→メソッド内でリスト.addを実行する→外部のリストにも反映されている(破壊的メソッドの使用)
- 複数の戻り値が欲しいならクラスでまとめて返す
6章
- 早期returnしよう
if(条件1) { // 1 == 1
if(条件2) {
if(条件3) {
// 処理
}
}
}
if(条件1) return; // 1 != 1
if(条件2) return;
if(条件3) return;
// 処理
if(条件1) {
val = 1;
} else if(条件2) {
val = 2;
} else if(条件3) {
val = 3;
} else {
val = 4
}
return val;
if(条件1) return 1;
if(条件2) return 2;
if(条件3) return 3;
return 4;
- あちこちで同じ条件分岐をせずに1か所にまとめよう
- インタフェース使おう
- 条件分岐の代わりにMapを使おう
enum ShapeType {
circle,
triangle,
square
}
switch(shapeType) {
case "円":
図形クラスを実装した円クラス.method();
case "三角":
図形クラスを実装した三角クラス.method();
case "四角":
図形クラスを実装した四角クラス.method();
default:
// 処理
}
final Map<ShapeType, Shape> shapes = new HashMap<>();
shapes.put(ShapeType.circle, 図形クラスを実装した円クラス);
shapes.put(ShapeType.triangle, 図形クラスを実装した三角クラス);
shapes.put(ShapeType.square, 図形クラスを実装した四角クラス);
void shapeMethod(final ShapeType shapeType) {
final Shape shape = shapes.get(shapeType);
shape.method();
}
7章
- stream使えるなら使おう
- 早期continue, breakしよう
- listは変更不要ならunmodifiableList()を使おう
8章
- 疎結合にしよう(所掌の切り分け)
- コードがほぼ同じだからといって複数機能を1機能にまとめないようにしよう
- 機能Aの場合はこの処理を追加、Bだけ、Cだけ、というのが増えていくと、まとめないほうがよかったってなる
- 継承より委譲しよう(コンポジション構造)
- 親となるクラスを継承するのではなく下記のようにしてインスタンス変数としてもつ
サブクラスはスーパークラスに依存しており、また、スーパークラスはサブクラスを気にせずに変更でもされるとサブクラスが壊れるため
- 親となるクラスを継承するのではなく下記のようにしてインスタンス変数としてもつ
private final 継承予定だったクラス 変数名;
- 高凝集にする際は別の概念が混ざって密結合にならないようにしよう
- 販売価格クラスに手数料や配送料、ポイントの処理を入れるのは関連してはいるけど販売価格とは違うため別の概念といえる
9章
- デッドコードは消そう。消すのが不安ならgitなどの差分管理を使おう
- マジックナンバーは定数にしよう
- nullで初期状態を表現せずに専用の値で初期状態を表現しよう
- 例外の握り潰しはやめよう
- 適切にフォルダ分け・パッケージ分けしよう
- MVCモデルならmodel, viw, controlerごととか
- 関連しあうビジネスクラスでまとめるとか
10章
- 目的ベースで名前を付けよう
- 存在ベース:商品
- 目的ベース:入庫品、予約品など
11章
- コメントも更新しよう
- コメント内容としてはロジックをなぞるのではなく、なぜこのロジックがあるのかとか修正する場合の注意点などを記載
12章
- 今までのおさらい
- エラーを戻り値で返さずにスローしよう
13章
- モデリングする際は所掌の切り分けをしよう
14章
- テストコードを書こう
- リファクタリングしよう
- リファクタリングと機能追加を同時にするのはやめよう
- バグの原因などがどちらにあるか調査を難しくさせるため
15章
- コード分析してくれるツールをいれよう
- レガシーコードによる悪影響のまとめ
16章
チーム開発でのノウハウについて主に記載されていました。
- コード重複とか発生するかもしれないからコミュニケーションをとろう
- クラス設計とレビューをしよう
- 厳密なクラス設計にこだわらず、実装して気づいたことをフィードバックするのもあり
- 設計ルールは多数決で決めないようにしよう
- レベルの低いほうに基準が合わせられることが多いため
- コーディング規約を利用しよう
- コードレビューをプルリクエストを使ってしくみ化しよう
- 設計ノウハウを使ってコード改善するなどのアウトプットをしよう
17章
- 設計などに関連するおすすめの技術本のまとめ
- インプットよりアウトプットを重視しよう
用語
-
低凝集
関連処理が分散している状態や、機能の所掌外の処理を持たせている状態 -
設計パターン(デザインパターン)
高凝集化したり、不正状態から防護するなどしてプログラムの構造を改造する設計手法 -
可変(ミュータブル)
-
不変(イミュータブル)
-
主作用
関数が引数を受け取り値を返すこと -
副作用
主作用以外の状態を変更すること -
ミューテーター
状態変更を発生させるメソッドのこと -
リポジトリパターン
DBロジックをカプセル化するパターン
アプリのロジックと分離できるため、DBロジックで汚さずに済む
たぶんMVCみたいな感じにすること -
横断的関心事
主に下記のようなどこでも使われるようなもの- ログ出力
- ログ出力
- エラー検出
- デバッグ
- 例外処理
- キャッシュ
- 同期処理
- 分散処理
-
ストラテジパターン
インタフェースで処理の切り替えをする設計のこと -
密結合
クラスに依存している状態 -
トランザクションスクリプトパターン
名前かっこいいけど、所掌の切り分けができずに長々と書いてる残念なコードのこと -
デッドコード
絶対に実行されない処理 -
YAGNI原則
必要な時に必要な処理を実装すること -
マジックナンバー
意図が不明な数値 -
null安全
ヌルぽを発生させない仕組み -
例外の握り潰し
catch文はあるけど何もしてないもの -
メタプログラミング
プログラム構造自体を制御するプログラム
リフレクション(javaの機能) -
退化コメント
メンテされず放置された結果、実装内容と一致していないコメント -
コマンド・クエリ分離(CQS)
変更または問い合わせのどちらかだけを行うように設計すること -
モデル
動作や仕組みなどの特徴や関係性を図式化したもの -
モデリング
モデル作成のこと -
ファーストクラスコレクション
コレクション関連のロジックをカプセル化する設計パターンのこと -
コードメトリクス(ソフトウェアメトリクス)
コードの品質指標のこと -
RuboCop
Rubyのコード解析ライブラリ
コード行数を違反すると警告される
デフォルトはメソッドだと10行まで、クラスだと100行まで -
循環的複雑度(サイクロマティック複雑度)
コードの複雑さの指標 -
Code Climate Quality
Code Climate社のコード品質分析ツール
GitHubと連携してリポジトリ内のコード品質をスコアリングしてくれる -
Understand
テマクトリックス者のコード品質分析ツール
メトリクスを計測してくれる -
レガシーコード
残念なコードのこと