Java | オブジェクト指向:可視性を最小にする

Java Java
スポンサーリンク

可視性を最小にするとは何か

「可視性を最小にする」は、
クラス・フィールド・メソッドのアクセス範囲(public / protected / なし / private)を
「必要な場所にだけ見えるよう、できるだけ狭くする」ことです。

Effective Java でも「クラスとメンバーのアクセス可能性は常に最小限にせよ」と強く推奨されています。
公開すればするほど、そのメンバーは「外から依存される API」になり、後から変えにくくなります。

まずは全部 private にしてみて、本当に必要になったものだけ、
package-private → protected → public の順に、最低限広げる癖をつけると安全です。


Java の可視性(アクセス修飾子)のざっくり整理

4つの可視性

Java の可視性は大きく 4 種類あります。

public
どこからでも見える(プロジェクト全体からアクセス可能)

protected
同じパッケージ+そのクラスを継承したサブクラスから見える

(修飾子なし:package-private)
同じパッケージ内からだけ見える

private
同じクラスの中からしか見えない

カプセル化(情報隠蔽)は、
「内部の実装を外から直接触れないようにし、必要な“入り口”だけ公開する」考え方です。
可視性を最小にするのは、カプセル化を守るための具体的な実践テクニックです。


典型的な悪い例:何でもかんでも public

public フィールドの危険さ

悪い例から見てみます。

public class BankAccount {

    public double balance;  // NG: 直接読み書きできてしまう

    public void deposit(double amount) {
        balance += amount;
    }

    public void withdraw(double amount) {
        balance -= amount;
    }
}
Java

balance が public なので、どこからでもこう書けてしまいます。

BankAccount acc = new BankAccount();
acc.balance = -1_000_000;  // マイナスや異常な値をいきなり代入できる
Java

これだと、

残高は 0 以上である
不正なマイナス残高は存在しない

といった「口座としての不変条件」を守れません。

本来、残高は deposit / withdraw のようなルール付きメソッドだけで変えるべきで、
balance フィールド自体は外から直接触れないようにすべきです。


良い例:まず private にして、必要な入り口だけ公開する(重要)

フィールドを隠し、メソッドで操作させる

先ほどの BankAccount を修正してみます。

public class BankAccount {

    private double balance;     // 外から直接はいじれない

    public BankAccount(double initialBalance) {
        if (initialBalance < 0) {
            throw new IllegalArgumentException("初期残高は0以上");
        }
        this.balance = initialBalance;
    }

    public double getBalance() {
        return balance;         // 読み取り用だけ公開
    }

    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("預入額は1以上");
        }
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("引き出し額は1以上");
        }
        if (amount > balance) {
            throw new IllegalStateException("残高不足");
        }
        balance -= amount;
    }
}
Java

ここでは、

balance フィールドは private
残高の変更は deposit / withdraw のルールを通さないとできない
読み取りは getBalance だけに制限

としています。

こうすると、外から balance を勝手にマイナスにされたり、
ルールをすっ飛ばして異常値が入ることを防げます。

「可視性を最小にする」とは、
「外から見えてほしくないものを、きちんと隠す」ことです。


なぜ可視性を最小にすると良いのか(深掘り)

変更しやすさ:公開 API は「後から変えにくい」

public メンバーは、一度公開すると「外のコードが依存する API」になります。

メソッド名
引数の数や型
戻り値の型

などを変えると、そのメソッドを呼んでいる全コードがコンパイルエラーになります。

つまり、public を増やすほど「互換性のために守らないといけない約束」が増えていきます。
逆に private なら、「そのクラスの中だけ」で完結しているので、好きなようにリファクタリングできます。

可視性を最小にすることは、そのまま「あとから変更する自由度を残す」ことにつながります。

カプセル化:内部実装を隠せる

内部実装を隠すと、
「外の人は使い方だけ知っていればよく、中でどうやっているかは気にしなくていい」
という状態を作れます。

例えば、BankAccount の残高計算ロジックを変えたくなったとしても、
public メソッドのシグネチャ(deposit, withdraw, getBalance)が変わらなければ、
外側のコードはそのまま動きます。

実装の変更が外に漏れないのは、可視性をしっかり絞っているからです。

誤用防止とセキュリティ

public にしてしまうと、「本当は呼んでほしくない使い方」をされるリスクが増えます。

内部用のメソッド
実験的なメソッド
前提条件を満たさないと危険なメソッド

こういったものが public だと、
「何のチェックもせずにいきなり呼ばれてしまう」かもしれません。

private / package-private にしておけば、
想定している範囲以外からはそもそも呼べないので、安全性が上がります。


「どの可視性を選ぶか」の具体的な判断の仕方

ステップ 1:まず全部 private で書いてみる

クラスを書き始めるときは、
フィールドもメソッドも、まずは全部 private で書いてみるのがおすすめです。

その上で、「このメソッドは他のクラスから呼ぶ必要があるな」となったときにだけ、
可視性を一つずつ広げていきます。

他クラスからも使うけど、同じパッケージの中だけで十分なら package-private(修飾子なし)。
継承先からだけ触れたいなら protected。
本当にどこからでも使わせたい API だけ public。

という順番で考えると、「とりあえず全部 public」病を防げます。

ステップ 2:パッケージを「モジュールの単位」として考える

Java では、アクセス修飾子なし(package-private)の可視性が結構重要です。

パッケージを「1つの小さなモジュール」と見なして、
その中だけで共有したいクラスやメソッドは package-private にする、という戦略です。

例えば、ドメイン層の中でだけ使う補助クラスや、
テストしたいけれど外には見せたくない内部クラスなどは、
あえて public にせず package-private にしておく、という設計もよく使われます。


よくある「やりすぎ/やらなさすぎ」パターン

何も考えずに public にする

「とりあえず他からも使うかもしれないから public」にしてしまうのは典型的なアンチパターンです。

public にしたその瞬間から、それは「変更に気を使うべき API」になります。
本当に外から使われる必要があるか、一度立ち止まってから決めるだけで、
設計の密度がかなり変わります。

逆に、全部 private かつ getter / setter だらけにする

もう一つありがちなのは

フィールドは全部 private
でも、全部に public getter / setter を生やす

というパターンです。

この場合、見かけ上はカプセル化しているようでいて、
実際には「フィールドを public にしているのとほぼ同じ」になってしまいます。

本当に外から書き換える必要があるのか?
単純 setter ではなく、意味のある操作メソッドにしたほうがよくないか?

といった観点で、「公開する入口」自体も最小限にしていくと、
クラスの不変条件と一貫性を守りやすくなります。


まとめ:可視性を最小にするための“自分への問い”

コードを書いていてアクセス修飾子を付けるとき、
毎回自分にこう問いかけてみてください。

このフィールド/メソッドを、本当に外のクラスから触る必要があるか
アクセス範囲は「クラス内 → パッケージ内 → サブクラス → 全体」のうち、どこまで必要か
これを public にしたら、将来仕様変更するときに困らないか
このメソッドは「使わせたい API」なのか、「ただの内部実装」なのか

迷ったら、少し厳しめ(より狭い可視性)から始めて、
コンパイルエラーが出たら必要なものだけ広げる、というやり方に慣れるといいです。

タイトルとURLをコピーしました