カプセル化の概念
カプセル化は、オブジェクト指向プログラミングの基本原則の1つで、データ(フィールド)とそのデータを操作するメソッドを一つのユニット(クラス)にまとめ、外部からの不正なアクセスからデータを保護する技術です。
クラス図
図1: カプセル化のクラス図
基本的なカプセル化の例
public class BankAccount {
// privateフィールド - 外部からの直接アクセスを防ぐ
private String accountNumber;
private double balance;
private String ownerName;
// コンストラクタ
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
// 公開メソッド - 安全なインターフェースを提供
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println(amount + "円が入金されました");
} else {
System.out.println("有効な入金額を指定してください");
}
}
public void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
System.out.println(amount + "円が出金されました");
} else {
System.out.println("出金できません:残高不足または無効な金額");
}
}
// アクセサメソッド(ゲッター)
public double getBalance() {
return balance;
}
public String getAccountNumber() {
return accountNumber;
}
public String getOwnerName() {
return ownerName;
}
// セッター(必要に応じて提供)
public void setOwnerName(String ownerName) {
this.ownerName = ownerName;
}
}
シーケンス図
図2: カプセル化の動作シーケンス
アクセス修飾子
Javaのアクセス修飾子を適切に使用することで、カプセル化を実現します:
修飾子 | クラス内 | 同一パッケージ | サブクラス | その他クラス |
---|---|---|---|---|
private |
○ | × | × | × |
デフォルト (修飾子なし) |
○ | ○ | × | × |
protected |
○ | ○ | ○ | × |
public |
○ | ○ | ○ | ○ |
カプセル化の利点
- データ保護:外部からの不正なアクセスや改変からデータを守ります
- 実装の柔軟性:内部実装を変更しても、外部インターフェースが同じであれば利用者に影響を与えません
- 使いやすさ:複雑な内部ロジックを隠蔽し、シンプルなインターフェースを提供できます
- コード管理:関連するデータとメソッドをまとめることで、コードの可読性と保守性が向上します
JavaBeansパターン
Javaではカプセル化の実践方法として、JavaBeansパターンが広く使われています:
- すべてのフィールドをprivateにする
- 各フィールドにアクセスするためのゲッター・セッターメソッドを提供する
- 引数のないデフォルトコンストラクタを用意する
- 必要に応じてSerializableインターフェースを実装する
JavaBeansパターンの例
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
private String address;
// デフォルトコンストラクタ
public Person() {
}
// ゲッターとセッター
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
実践的なポイント
- フィールドは原則としてprivateにする
- セッターメソッドでは入力値の検証を行う
- 変更されるべきでないフィールドにはセッターを提供しない
- コレクションを返すゲッターは、防御的コピーを考慮する(例:
return new ArrayList<>(myList);
) - immutable(不変)クラスを設計する場合は、すべてのフィールドをfinalにし、セッターを提供しない