[読書メモ]リファクタリング - 委譲の隠蔽

# 委譲とは
クラス拡張方法の一種。
クラスを拡張する方法は以下の二種類ある。
1. 継承
2. 委譲

## 継承と委譲の違い

### 継承ってどんなもの？
Javaでは`extends`で表されている。
```javaの継承
class A extends B {

}
```
Railsでは`<`という記号で表される。
```Railsの継承
class A < B

end
```
#### 継承の効果
継承を使うと、子クラスは親の持つフィールド、メソッドを全て受け継ぎます。

```継承の例
class Living {
  private int age;

  public Living(int age) {
    this.age = age;
  }

  public boolean isUnder18() {
    return age < 18;
  }
}

class Person extends Living {
  private String name;

  public Person(String name, int age) {
    super(age);
    this.name = name;
  }

  public getName() {
    return name;
  }
}

public static void main(String[] args) {
  Person person = new Person("Sugita", 17);
  System.out.println("This guy is under age? : " + person.isUnder18); // => true
}
```

上記のプログラムは、PersonクラスはLivingクラスを継承しています。
Personクラスには明示的に`Person#isUnder18`メソッドは宣言されていませんが、親クラスであるLivingクラスに宣言されているメソッドであるため使用することが可能です。

### 委譲ってどんなもの？
委譲とは以下のような形で利用されます。
```委譲の例
class Thing {
  int age;
  
  public Thing(int age) {
    this.age = age;
  }
  
  public boolean isUnder18() {
    return age < 18;
  }
  
  public void decrementAgeBy(int num) {
    this.age -= num;
  }
}

class Person {
  private Thing thing;
  private String name;
  
  public Person(int age, String name) {
    this.thing = new Thing(age);
    this.name = name;
  }

  public boolean isUnder18() {
    return thing.isUnder18();
  }

  public String getPersonInfo() { 
    return "name: " + name + ", age: " + age; 
}

public static void main(String[] args) {
  Person person = new Person(19, "Sugita");
  person.isUnder18(); // => false
  person.getPersonInfo(); // => "name: Sugita, age: 19"
  person.decrementAgeBy(10); // => メソッド呼び出し不可能
}
```

あるクラスAを拡張したいとします。
そのクラスを他のクラスBのフィールドとして保持します。
そしてBに追加したいメソッドなどを追加します。
もともとAにあったメソッドなどはBが保持しているフィールドAに対して呼び出します。
ただ、`Person#decrementAgeBy(int num)`の呼び出しは失敗します。
なぜなら、このメソッドはPersonに宣言されていないからです。

### どう使い分けるのか？

#### IS_AとHAS_Aで使い分ける
一般的には使い分けに際して、AとBが以下のような基準が用いられているようです。
- `IS_A`の関係なら継承
- `HAS_A`の関係なら委譲

さっきの例で言えば、
- LivingとPersonは`IS_A`なので継承
- ThingとPersonは`HAS_A`なので委譲
です。

この基準が有効である理由としては、
`IS_A`の関係であれば、親クラスが今後拡張されても、子クラスと全く関係ないものが追加される可能性が少ないためです。
例えば、Livingに今後性別を性別を判別するメソッドが追加されても、子クラスであるPersonにもそれが存在することには違和感がありません。

しかし、Thingにある`decrementAgeBy(int num)`はPersonにあるとおかしいですね。
Personは若返らないので。

#### インターフェースに公開するメソッドで使い分ける
継承を使用すると、全てのメソッドが継承先のクラスに引き継がれます。

しかし、委譲を使用した場合自分で公開するメソッドを決めることができます。
公開するメソッドに関しては、インターフェースを宣言し、実装はフィールドのクラスのメソッドを呼び出せば良いだけなので。
ただ、公開するメソッドが多すぎるとこの委譲の処理をたくさん描かないといけなくなるので、辛いですね。

# なぜ隠蔽するのか
クラス同士の結合が増えるのは基本的にはよくありません。
一つのクラス（例えばA）が知ってる外部クラスが多いほど、Aの修正の影響を受ける可能性があるファイルの数が増えるからです。
疎結合にして、一つのファイルの変更の影響範囲を最小限に留めたいのがOOPです。

で、例えば委譲を隠蔽していないパターンは以下の通りです
```委譲を隠蔽していない
class Department {
  Person manager;
  
  public Department(Person manager) {
    this.manager = manager;
  }

  public getManager() { 
    return this.manager;
  }
}

class Person {
  Department department;
  
  public Department getDepartment() {
    return this.department;
  }
}

// ↓クライアントコード
public static void main(String[] args) {
  // ある人のマネージャーを知りたい
  manager = john.getDepartment().getManager();
}
```
上記の例だと、PersonクラスがDepartmentを持っていることをクライアント走ってしまっています。
これだと密結合です。
たとえばDepartmentクラスの命名が変更されただけでも、クライアントコードも変更しなければならないですね。

これを隠蔽することで、より疎結合なコードを作り、変更しやすくできるのです。

```委譲を隠蔽している
class Department {
  Person manager;
  
  public Department(Person manager) {
    this.manager = manager;
  }

  public getManager() { 
    return this.manager;
  }
}

class Person {
  Department department;
  
  public Person getManager() {
    return this.department.getManager();
  }
}

// ↓クライアントコード
public static void main(String[] args) {
  // ある人のマネージャーを知りたい
  manager = john.getManager();
}
```