この記事は、保守性の高いコード設計シリーズの一部です。前回の記事「【第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);
}
}
この設計により、先ほどの問題はすべて解決されます。
- データ保護:
balance
はprivate
なので、deposit
やwithdraw
といった決められたメソッドを通さなければ変更できません。不正な値のセットを防げます。 - ロジックの集約: 「残高はマイナスにならない」というルールは、
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
自身が「入金する」「出金する」という知識を持っているため、私たちはただお願いするだけで良くなりました。これがオブジェクト指向の基本的な考え方です。
まとめ
クラスは、単にコードをまとめるための構文ではありません。データとそのデータに関する責任(ロジック)を一つのカプセルに閉じ込めることで、コードを安全で、理解しやすく、そして再利用可能な「部品」へと進化させるための、非常に強力な設計ツールです。
変数の命名から始まり、メソッドの切り出し、そしてクラスへの集約というステップを踏むことで、あなたのコードは格段に保守性の高いものになるでしょう。
▼「保守性の高いコード設計」シリーズ このシリーズでは、読みやすく保守性の高いコードを書くためのテクニックをご紹介しました。ぜひ他の記事もご覧ください。
- 【第1部】 読みやすいコードの第一歩:変数とメソッドの正しい扱い方
- 【第2部】 データとロジックをまとめよう:クラスで実現する、堅牢なコード設計(この記事)