2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

読書感想『良いコード悪いコードから学ぶ設計入門』

Last updated at Posted at 2025-09-28

定番の技術書を読了したため、
個人的に見返したい内容を記録しつつ、復習します。
(*本記事でまとめる内容は改訂新版の前のものです。)

副作用は無くすこと

関数(メソッド)の話。

副作用とは

関数が引数を受けとり、戻り値を戻す以外に、外部の状態を変更すること

「外部の状態」はいくつか対象となる。引数、インスタンス変数、グローバル変数、ファイルIOなど。関数内でこれらを容易に変更しないこと。
なおローカル変数は変更してもok。関数内に留まり影響しないため。

  • 問題点

    • 影響範囲を広げてしまう。副作用を起こしているコードは意外にも特定しづらく、原因特定の手間もかかる
  • 対策

    • 引数を不変 / 読み取り専用にする
    • アクセス制限を決める

悪い例
ゲームプレイヤーを想定しKnightクラスを作成する。
致命的に攻撃(attckメソッド)するたびに強くなってしまう。こういう混入は避けたい。

public class Knight {
    int power = 10;  
    void attack() {
        System.out.println("attack! : " + power);
        power += 10;
    }
}

呼び出す側

public class App {
    public static void main(String[] args) throws Exception {
        Knight knight = new Knight();
        knight.attack();
        knight.attack();
    }
}

// 出力結果
// attack! : 10
// attack! : 20

関数は命じる形にする

これも関数の話。
クラスやオブジェクトなど、使う人(呼び出す側、Aとする)が使う部品(呼び出される側、Bとする)の内部状態を把握したり、それに応じて制御や判断しないこと。
尋ねるな、命じろ(Tell, Don't Ask)という格言があるほど意識したいこと。

  • 問題点

    • B(部品)を変更した時、A(使う人)が影響を受けてしまう。影響範囲が広くなってしまう。
    • AがBの内部処理を少し知る必要があり、やや依存している。Aが大変≒責務が増えている。
  • 対策

    • Bが状態の変更、制御、判断を行う = 命じるメソッドにする
    • Aが知らなくても、Bが自律的に動けるようにする

悪い例
呼び出す側Appクラス
呼び出される側CoffeeMachineクラスの状態(水と豆があるか)をチェックしている。

public class App {
    public static void main(String[] args) throws Exception {
        CoffeeMachine machine = new CoffeeMachine();

        if (machine.isWaterFilled()) {
            if (machine.isBeanFilled()) {
                machine.makeCoffee();
            }
            return;
        }
    }
}
public class CoffeeMachine {
    boolean isWaterFilled = true;
    boolean isBeanFilled = true;

    boolean isWaterFilled() {
        return isWaterFilled;
    }

    boolean isBeanFilled() {
        return isBeanFilled;
    }

    void makeCoffee() {
        System.out.println("Making Coffee");
    }
}

良い例
状態のチェックはCoffeeMachineクラスが担う。
これでボタン1つ押せばコーヒーが作れる。

public class App {
    public static void main(String[] args) throws Exception {
        CoffeeMachine machine = new CoffeeMachine();
        machine.makeCoffee();
    }
}
public class CoffeeMachine {
    boolean isWaterFilled = true;
    boolean isBeanFilled = true;

    void makeCoffee() {
        if (!isWaterFilled) {
            System.out.println("Fill water tank");
            return;
        }
        if (!isBeanFilled) {
            System.out.println("Fill bean tank");
            return;
        }
        System.out.println("Making Coffee");
    }
}

コマンドとクエリを分離する

これも関数の話。
関数は、次の2つのどちらかにしましょうという考え方。

  • コマンド:状態を変更する
  • クエリ:状態を返す、問い合わせ

ちなみにどちらも同時に行うと「モディファイア」と呼ぶ。

悪い例
変数pointに加算し返している

int AddAndGetPoint() {
	point += 10;
	return point;
} 

良い例
pointへの加算と、pointの取得を分離する

void AddPoint() {
	point += 10;
}

int GetPoint() {
	return point;
} 

命名を「名前設計」と呼んでみる

クラス名やメソッド名を検討することを「命名」という。
これを「名前設計」と呼ぶ、あるいは捉える。

ここでの設計は、

「ある課題を解決するためのしくみや構造を考えたり、作り上げたりすること」

と定義している。

命名を設計行為の一貫と考えることで、単純に名前をつける作業よりも、
そのクラスやメソッドの作り方まで意識が広がる。

その他

  • コメント
    • 仕様変更の注意点を書くのもgood
  • マズローのハンマー
    • 新しい知識や技術をついつい使ってみたくなる現象
    • 「ハンマーを持っているとなんでも釘に見えちゃう」ということから、わりとわかる
  • 凝集度と結合度
    • まず、モジュールはクラス、パッケージ、レイヤーなど様々な粒度に解釈できる
    • 凝集度は、モジュール内におけるデータとロジックの関連性の強さ
      • クラス粒度だと、インスタンス変数と、それを変更するメソッドがクラス内に定義されているか
    • 結合度は、モジュール間の依存の度合い
      • クラス粒度だと、呼び出すクラスと呼び出されるクラスの数など

感想

  • 悪い例に引数を書き換えるコードを示せそうとしましたが、javaでは値渡しがデフォルトでした
  • 他にもメモしたいことは別の記事にまとめたいと考えております
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?