【第2部】 データとロジックをまとめよう:クラスで実現する、堅牢なコード設計

この記事は、保守性の高いコード設計シリーズの一部です。前回の記事「【第1部】 読みやすいコードの第一歩:変数とメソッドの正しい扱い方」では、変数とメソッドを整理し、コードを読みやすくする方法を解説しました。

こんにちは。前回の記事では、変数やメソッドを整理することで、コードの可読性を高める方法を学びました。しかし、その時点では、データ(変数)と、そのデータを操作するロジック(メソッド)は、まだ別々の場所にありました。

今回は、それらを「クラス」という強力な仕組みを使って一つにまとめることで、いかにコードが安全で、再利用しやすくなるかを見ていきましょう。

目次

問題点:データとロジックが離れていると何が起きるか

銀行口座の残高を管理するシステムを例に考えてみましょう。クラスを使わない場合、データとロジックは以下のように散らばってしまいます。

【Before】データとロジックがバラバラの状態

// 口座残高。ただの変数としてどこかに定義されている
BigDecimal accountBalance;

// ■どこかに書かれている「入金」のロジック
// accountBalanceに直接、入金額を加算している
accountBalance = accountBalance.add(depositAmount);

// ■また別の場所に書かれている「出金」のロジック
// 直接、残高から引いているため、残高がマイナスになる可能性がある
accountBalance = accountBalance.subtract(withdrawalAmount);
if (accountBalance.compareTo(BigDecimal.ZERO) < 0) {
    // マイナスになってしまった場合の処理…
    accountBalance = BigDecimal.ZERO;
}

この設計には、いくつかの重大な問題があります。

  • 不正な状態になりうる: accountBalanceはただの変数なので、誰でも直接 accountBalance = new BigDecimal("-10000"); のように、ありえない値(マイナスの残高)をセットできてしまいます。
  • ロジックの重複: 入金や出金のロジックが色々な場所にコピー&ペーストされ、修正漏れの原因になります。
  • 仕様が分からない: 「残高はマイナスになってはいけない」という重要なビジネスルールが、コードのあちこちに分散し、全体像を把握できません。

解決策:関係し合うデータとロジックをクラスにまとめる

これらの問題を解決するのが「クラス」です。クラスは、強く関係し合うデータ(状態)とそのデータを操作するロジック(振る舞い)を、一つのまとまりとしてカプセル化するための設計図です。

先ほどの銀行口座の例を、クラスを使って書き直してみましょう。

【After】データとロジックを「BankAccount」クラスにまとめる

class BankAccount {
    // データ(残高)は外部から直接変更できないようにprivateにする
    private final BigDecimal balance;

    // クラスの外からは、このコンストラクタ経由でしかインスタンスを作れない
    BankAccount(final BigDecimal balance) {
        // 不正な値(マイナスの残高)で初期化されないようにチェック
        if (balance.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("残高は0以上である必要があります");
        }
        this.balance = balance;
    }

    // 「入金する」というロジック(振る舞い)
    BankAccount deposit(final BigDecimal amount) {
        final BigDecimal deposited = balance.add(amount);
        // 新しい残高を持つ、新しいBankAccountインスタンスを返す
        return new BankAccount(deposited);
    }

    // 「出金する」というロジック(振る舞い)
    BankAccount withdraw(final BigDecimal amount) {
        final BigDecimal withdrawn = balance.subtract(amount);
        // ここでも、残高がマイナスにならないことを保証
        final BigDecimal corrected = withdrawn.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : withdrawn;
        return new BankAccount(corrected);
    }
}

この設計により、先ほどの問題はすべて解決されます。

  • データ保護: balanceprivateなので、depositwithdrawといった決められたメソッドを通さなければ変更できません。不正な値のセットを防げます。
  • ロジックの集約: 「残高はマイナスにならない」というルールは、withdrawメソッドとコンストラクタの中に集約されており、仕様が明確です。
  • 再利用性: BankAccountクラスは、安全で信頼できる「部品」として、システムのどこからでも安心して利用できます。

このクラスを使う側のコードは、非常にシンプルになります。

// 10000円の残高を持つ口座を作成
BankAccount myAccount = new BankAccount(new BigDecimal("10000"));

// 3000円を入金する
myAccount = myAccount.deposit(new BigDecimal("3000"));

// 5000円を出金する
myAccount = myAccount.withdraw(new BigDecimal("5000"));

myAccount自身が「入金する」「出金する」という知識を持っているため、私たちはただお願いするだけで良くなりました。これがオブジェクト指向の基本的な考え方です。

まとめ

クラスは、単にコードをまとめるための構文ではありません。データとそのデータに関する責任(ロジック)を一つのカプセルに閉じ込めることで、コードを安全で、理解しやすく、そして再利用可能な「部品」へと進化させるための、非常に強力な設計ツールです。

変数の命名から始まり、メソッドの切り出し、そしてクラスへの集約というステップを踏むことで、あなたのコードは格段に保守性の高いものになるでしょう。

▼「保守性の高いコード設計」シリーズ このシリーズでは、読みやすく保守性の高いコードを書くためのテクニックをご紹介しました。ぜひ他の記事もご覧ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次