4
4

オブジェクト指向のカプセル化とは?

Posted at

なぜ調べたいと思ったか?

クラスのフィールド変数に、アクセスを制限する修飾子をつけてカプセル化してきた。ただ、アクセスをどうやって制限しているかが分からなかった。

カプセル化をイメージすると・・

まずは、薬局にあるカプセル状の薬をイメージしてみましょう。
encapslation_1.jpg
画像参照元:https://webpia.jp/encapsulation/

・液体や粉状の薬を、容器で包んだもの

をイメージしたのではないでしょうか?
実はプログラミングのカプセル化も、その様なイメージなんです。オブジェクト指向のカプセル化を理解するには、以下の3ステップの理解が重要だと思っています。

STEP1 :「何を包んで何から守るか?」
STEP2 :「カプセル化したデータを変更するには?」
STEP3 :「データを直接変更してはいけないのはなぜ?」

STEP1:何を包んで何から守るか?

「オブジェクトにあるデータ」を包んで、「オブジェクト外からの不正アクセス」から守るのが答えですが、これでは理解できません。カプセル化するとしないで、どんな違いがあるのでしょうか?

そこでJava言語を例に、カプセル化の機能を持つprivate修飾子と、その機能を持たないpublic修飾子で、どんな違いがあるのか調べてみました。

Car.java
class Car {
  private String name;
  private String color;
  public int distance = 0;
  public int fuel = 100;
}

「name」と「color」はデータ1、「distance」と「fuel」はデータ2に該当します。

カプセル化-カプセル化-イメージ2.drawio-3.jpg
画像参照元:https://webpia.jp/encapsulation/

データ1はカプセル化によって、オブジェクト外からのアクセスを禁止していますが、データ2はアクセス出来てしまいます。

「オブジェクト外からのアクセスを禁止すると、カプセル化したデータを変更出来ないんじゃ・・」
と思いますが、そんな事はありません。

STEP2:カプセル化したデータを変更するには?

データをカプセル化したなら、カプセル化していない関数を用意して、データを変更すれば良いんですね。

Car.java
class Car {
  private String name;
  private String color;
  public int distance = 0;
  public int fuel = 100;
  
 Car(String name, String color) { # Carクラスの生成と同時にカプセル化したデータを代入)
    this.name = name;
    this.color = color;
  }
  
  public String getName() { # カプセル化したデータのnameを取得
    return this.name;
  }
  
  public String getColor() { # カプセル化したデータのcolorを取得
    return this.color;
  }
}

クラス名が同じのコンストラクタで、カプセル化したデータを変更しています。

カプセル化-カプセル化-イメージ3.drawio.jpg
画像参照元:https://webpia.jp/encapsulation/

そして、getから始まるゲッタメソッドで、カプセル化したデータを取得できる様になりました。

関数を用意してデータを変更できましたが、ここで最大の謎である「データを直接変更していけないのはなぜ?」が浮かんできませんか?

STEP3:データを直接変更してはいけないのはなぜ?

Car.java
class Car {
  private String name;
  private String color;
  public int distance = 0;
  public int fuel = 100;
}

数値型データを例にすると、データを直接変更してはいけない理由がわかると思います。

燃料満タンの意味で、「fuel」に100を代入しました。
ところが、負数や100より大きい数値が代入され、データの整合が取れなくなりました。
オブジェクト外から自由にデータ変更できる状態だったのが理由なので、

・データのカプセル化
・データの許容を制限する関数の用意

が必要なんですね。以上のことを踏まえて、Carクラスの内容を変更しました。

Car.java
class Car {
  private String name;
  private String color;
  private int distance = 0;
  private int fuel = 100;
  
  Car(String name, String color) {
    this.name = name;
    this.color = color;
  }
  
  public String getName() {
    return this.name;
  }
  
  public String getColor() {
    return this.color;
  }
  
  public int getDistance() {
    return this.distance;
  }
  
  public int getFuel() {
    return this.fuel;
  }
  
  # ガソリンを消費して車を走行する
  public void run(int run_distance) {
    System.out.println(run_distance + "km走ります");
    if (run_distance <= this.getFuel()) {
      this.distance += run_distance;
      this.fuel -= run_distance;
    } else {
      System.out.println("ガソリンが足りません");
    }
    System.out.println("走行距離:" + this.getDistance() + "km");
    System.out.println("ガソリン量:" + this.getFuel() + "L");
  }
  
  # ガソリンを補充する
  public void charge(int fuel_amount) {
    System.out.println(fuel_amount + "L給油します");
    if(fuel_amount <= 0) {
      System.out.println("給油できません");
    } else if((this.fuel + fuel_amount) >= 100) {
      System.out.println("満タンまで給油します");
      this.fuel = 100;
    } else {
      this.fuel += fuel_amount;
    }
    System.out.println("ガソリン量:" + this.fuel + "L");
  }
}

・ガソリンを消費して車を走行するメソッド
・ガソリンを補充するメソッド

上記のガソリンを補充するメソッドに注目すると、「負数を許可しない」「燃料満タンを超えての給油はできない」といった制限がありますね。

調べてみて分かったこと

「カプセル化は、オブジェクト外からデータを直接変更するのを禁止する」とあったけど、想定していない数値を設定するケースを考えたら、その意味がようやく分かりました。

4
4
1

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
4
4