0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

java学習9 オブジェクト指向⑦ カプセル化

0
Posted at

カプセル化を理解するための前提知識

各用語の意味を再確認

そして命名規則についても再確認

言語 メソッドの記名方式
java キャメルケース
python スネークケース

javaにおける慣例の命名規則ほか

対象 表記
変数 小文字
定数 大文字
クラス名 頭文字は大文字
メソッド名 頭文字は小文字のキャメル方式

javaではソースファイル名とmainメソッドを持つクラス名は同じしないといけない厳格なルールがある。さらにmainメソッドをもつクラスはpublicをつける必要がある。

ここまでを前提知識としてここからが本題のカプセル化についてのまとめ

カプセル化とは

大切な情報(フィールド)や操作(メソッド)にアクセス制御をかけることにより、悪意や間違いによるメンバの利用を防止し、想定しない利用が発生したときには、その原因箇所を特定しやすくするためのしくみ

メリット

  • read only, write onlyのフィールドを実現できる
  • クラスの内部設計を自由に変更できる
  • フィールドへのアクセスを検査できる → 最適化(リファクタリング)に繋がる

アクセス制御

「何に・何を・どこまで制限するか」ということ。
原則として、データ(フィールド)をクラス外から自由に書き換えられるべきではなく、クラスが提供するメソッドを通じて安全に変更・取得されるべきであるという考え方。
そのため、設計の基本は

  • フィールドは原則 private (データを隠蔽し、不正な書き換えを防ぐ)
  • 操作窓口となるメソッドは public (外部との安全な取引メッセージ)
  • 内部処理用のメソッドは private (外に見せる必要のない仕組みは隠す)

実際のアクセス制御を行ううえでは、下記のアクセス修飾子を使って制限のレベルを選択する。

制限のレベル 名称 アクセス修飾子 アクセスを許可する範囲
制限が厳しい private private 自分自身のクラスのみ
↑ package private なし 自分と同じパッケージに属するクラス
↓ protected protected 自分と同じパッケージに属するか、自分を継承した子クラス
制限が緩い public public すべてのクラス

注: 修飾子を何も書かない状態を「パッケージプライベート(またはデフォルト)」と呼び、同じパッケージに属するクラスからのみアクセスを許可する設定ということ

実例

まずはこれまで述べたカプセル化の原則を行わずに作成されたクラスで発生する問題を以下のコード例として再確認。

// 銀行口座を表すクラス(カプセル化されていない)
class BankAccount {
    // フィールドがpublicなので、どこからでも自由に書き換えられてしまう
    public String ownerName;
    public int balance;

    public BankAccount(String ownerName, int balance) {
        this.ownerName = ownerName;
        this.balance = balance;
    }
}

public class Main {
    public static void main(String[] args) {
        // 山田さんの口座を1万円で開設
        BankAccount account = new BankAccount("山田太郎", 10000);

        // 問題点①:チェックをすり抜けて「不正な値(マイナス)」にできてしまう
        account.balance = -500000; 

        // 問題点②:クラスのルール(秩序)を無視して自由に書き換えられてしまう
        account.ownerName = "謎のハッカー"; 

        System.out.println("口座名義: " + account.ownerName);
        System.out.println("残高: " + account.balance + "円"); 
        // 出力結果:口座名義: 謎のハッカー、残高: -500000円 (大問題!)
    }
}

以上のように問題点が2点確認できるが、そもそもこのような問題が起きないようにクラスを設計することが重要。以下のコードはこれらの問題が発生しないようにクラスを設計し直した例である。

// 銀行口座を表すクラス(正しくカプセル化されている)
class BankAccount {
    // フィールドをprivateにして、外部から直接書き換えられないように隠蔽する
    private String ownerName;
    private int balance;

    public BankAccount(String ownerName, int balance) {
        this.ownerName = ownerName;
        // 初期化時もマイナス残高にならないよう、安全のためにメソッド経由でセットする
        setBalance(balance);
    }

    // 名義を取得するメソッド(Getter)
    public String getOwnerName() {
        return ownerName;
    }

    // 残高を取得するメソッド(Getter)
    public int getBalance() {
        return balance;
    }

    // 残高を変更するメソッド(Setter)
    public void setBalance(int balance) {
        // クラス自身のルール:残高がマイナスになる変更は拒否する
        if (balance < 0) {
            System.out.println("【エラー】残高をマイナスにすることはできません。処理を拒否しました。");
            return; // 変更せずに処理を終了する
        }
        this.balance = balance;
    }
}

public class Main {
    public static void main(String[] args) {
        // 山田さんの口座を1万円で開設
        BankAccount account = new BankAccount("山田太郎", 10000);

        // 改善ポイント①:直接書き換えようとするとコンパイルエラーになる
        // account.balance = -500000; // コメントを外すとエラー:balanceはprivateです

        // 改善ポイント②:メソッドを経由させることで、不正な操作をブロックできる
        account.setBalance(-500000); // 画面にエラーメッセージが表示され、残高は守られる

        // 改善ポイント③:名義を変更するメソッド(Setter)を用意しなければ、外から書き換えられない
        // account.ownerName = "謎のハッカー"; // エラー:ownerNameはprivateです

        System.out.println("口座名義: " + account.getOwnerName());
        System.out.println("残高: " + account.getBalance() + "円");
        // 出力結果:口座名義: 山田太郎、残高: 10000円 (安全に守られた!)
    }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?