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

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


## カプセル化をイメージすると・・
まずは、薬局にあるカプセル状の薬をイメージしてみましょう。
![encapslation_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568357/191014b4-1635-7ccb-f01b-5e67565f9e5c.jpeg)
画像参照元：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://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568357/6427c6be-fd09-45af-60d2-df8da1e78d37.jpeg)
画像参照元：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://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3568357/887a9a50-4db5-15b6-26c5-85b5be624b4c.jpeg)
画像参照元：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");
  }
}
```
・ガソリンを消費して車を走行するメソッド
・ガソリンを補充するメソッド

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


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