Java | 1 日 120 分 × 7 日アプリ学習 中級編:オブジェクト指向(OOP) - カプセル化アプリ

Web APP Java
スポンサーリンク

1日目のゴール

中級編「カプセル化アプリ」1日目のテーマは
「直接触らせない」=カプセル化の感覚を体でつかむこと です。

キーワードは
privategetter / setter

でも、ただ「書き方を覚える」んじゃなくて、
「なぜ直接触らせないほうがいいのか」 を、
ちゃんと腑に落ちるところまで行きます。


カプセル化って、そもそも何なのか

「中身を勝手にいじらせない」という設計の考え方

カプセル化(encapsulation)は、
ざっくり言うとこういう考え方です。

「オブジェクトの中身(フィールド)を、
外から勝手にいじらせないようにする。
代わりに、“決められた窓口(メソッド)”だけを通して
触ってもらう。」

イメージで言うと、
薬のカプセルみたいなものです。

中身の粉は直接触れない。
外から見えるのは「カプセル」という殻だけ。
飲み方(使い方)は決まっている。

プログラムでも同じで、
「中身を守る殻」 を作るのがカプセル化です。


悪い例から入る:フィールドを丸出しにしたクラス

直接触れると、何が困るのか

まず、あえて「悪い例」を見てみます。

public class User {
    public String name;
    public int age;
}
Java

このクラスは、
nameagepublic です。

つまり、どこからでもこう書けます。

User u = new User();
u.name = "Taro";
u.age = -100;  // これも通ってしまう
Java

ここで問題になるのは、

年齢がマイナスでも通ってしまう
ありえない値でも、誰でも勝手に入れられる

ということです。

「User の中身を守る人」がいない 状態なんです。


「直接触らせない」ための第一歩:private

フィールドを隠す=外から見えないようにする

そこで出てくるのが private です。

public class User {
    private String name;
    private int age;
}
Java

private を付けると、
そのフィールドは 「そのクラスの中からしか触れない」
というルールになります。

外側からこう書こうとすると、コンパイルエラーになります。

User u = new User();
u.name = "Taro";  // エラー
u.age = 20;       // エラー
Java

これは一見「不便」に見えるかもしれませんが、
実はめちゃくちゃ大事な一歩です。

「中身に直接触れないようにすることで、
中身を守るチャンスが生まれる」
からです。


「触る窓口」を用意する:getter / setter

ただ隠すだけじゃなく、「正しい触り方」を決める

private にしただけだと、
外から何もできなくなってしまいます。

そこで出てくるのが
gettersetter です。

public class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
Java

こうすると、外からはこう書けます。

User u = new User();
u.setName("Taro");
u.setAge(20);

System.out.println(u.getName());
System.out.println(u.getAge());
Java

ここで重要なのは、

フィールドには直接触れない
でも、決められたメソッドを通してなら触っていい

という 「ルール付きのアクセス」 に変わったことです。


重要ポイントの深掘り1:setter の中で“守り”ができる

「ありえない値」を弾けるようになる

public フィールドのときは、
誰でも好きな値を入れられました。

age = -100 も通ってしまう。

でも、setter を使うと、
その中でチェックができます。

public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年齢は0以上にしてください");
    }
    this.age = age;
}
Java

こうしておけば、

User u = new User();
u.setAge(-100);  // ここで例外が出る
Java

「User の中身を守るのは User 自身」
という状態になります。

これがカプセル化の本質です。


重要ポイントの深掘り2:内部表現を後から変えられる

外から見える“顔”はそのまま、中身だけ差し替える

もう一つ、カプセル化の大きなメリットがあります。

それは、
「中身の表現を後から変えても、外側のコードを壊さなくて済む」
ということです。

例えば、最初はこう書いていたとします。

public class User {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("年齢は0以上");
        }
        this.age = age;
    }
}
Java

でも、途中で
「年齢じゃなくて、生年月日で持ちたいな」
と思ったとします。

内部をこう変えます。

public class User {
    private LocalDate birthDate;

    public int getAge() {
        LocalDate now = LocalDate.now();
        return Period.between(birthDate, now).getYears();
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }
}
Java

外側のコードはどうでしょう。

User u = new User();
u.setBirthDate(LocalDate.of(2000, 1, 1));
System.out.println(u.getAge());
Java

「年齢を知りたい」という外側のニーズは
getAge() というメソッドで満たされ続けます。

「外から見えるメソッド(顔)は変えずに、
中身だけ差し替える」
ことができる。

これが、
カプセル化が保守性を上げる理由です。


例題:カプセル化アプリの超シンプル版

「ポイントカード」クラスで考えてみる

小さな「ポイントカード」クラスを作ってみましょう。

やりたいことはこうです。

ポイントを貯める
ポイントを使う
現在のポイントを確認する

でも、
「直接ポイント数をいじらせない」
という方針で設計します。

public class PointCard {
    private int point;

    public PointCard() {
        this.point = 0;
    }

    public int getPoint() {
        return point;
    }

    public void addPoint(int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("追加ポイントは0以上");
        }
        this.point += amount;
    }

    public void usePoint(int amount) {
        if (amount < 0) {
            throw new IllegalArgumentException("使用ポイントは0以上");
        }
        if (amount > point) {
            throw new IllegalArgumentException("ポイントが足りません");
        }
        this.point -= amount;
    }
}
Java

外側からはこう使います。

PointCard card = new PointCard();
card.addPoint(100);
card.usePoint(30);
System.out.println(card.getPoint()); // 70
Java

ここでのポイントは、

point フィールドは private
外からは addPointusePoint しか使えない
「マイナスを追加する」「残高以上を使う」などの不正操作は
クラス自身が弾いてくれる

という 「直接触らせないことで、ルールを守らせる」 設計になっていることです。


今日のまとめ:カプセル化の“感覚”

1日目で掴んでほしいのは、
privategetter / setter の書き方そのものよりも、
「なぜ直接触らせないほうがいいのか」
という感覚です。

フィールドを public にすると、
誰でも好き勝手にいじれてしまう。

private にして、
getter / setter を通すことで、

値のチェックができる
内部表現を後から変えられる
クラス自身が自分の中身を守れる

という状態になる。

それがそのまま
保守性=壊れにくさ・変更に強さ
につながっていきます。

次のステップでは、
「ただの getter / setter」から一歩進んで、
「振る舞いを持ったメソッド」 にしていく話にも
つなげていけます。

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