[Java] デザインパターン - Proxy

## 目的

以下の書籍を読んで、デザインパターンについて学習したため、そのアウトプットとして記事を作成する。

- Java言語で学ぶデザインパターン入門 第3版 (著: 結城浩)


## Proxyパターンとは

- Proxy: 代理人の意味
- 本人でなくてもできるような仕事を代理人に任せる。代理人ができる範囲を超えたら本人のところへ行って相談するイメージ
- (例) 初期化に時間がかかる機能がある大きなシステム
  - 一度初期化ボタンを押すと毎回使わない機能を含めた全機能を初期化していたら、アプリケーションの起動に必要以上の時間がかかってしまう。
  - 初期化に必要なものだけを「代理人」が実行し、各機能に必要な初期化を「本人」が行うようにするとアプリケーションの起動スピードが向上する


## 実際に使ってみる
アイドルとマネージャーの関係がそれっぽかったので、それらの関係性をProxyパターンで表してみた。
### クラス図

```mermaid
classDiagram

Entertainer <|.. PopIdle
Entertainer <|.. PopIdleProxy
PopIdle *-- PopIdleProxy
class Entertainer {
  <<interface>>
  +setFunName()* String
  +getFunList()* ArrayList<String>
  +adjustSchedule()* void
  +writeAutograph()* void
}
class PopIdle {
  -String idleName
  -ArrayList<String> funList
  +setFunName() String
  +getFunList() ArrayList<String>
  +adjustSchedule() void
  +writeAutograph() void
  -orderPending() void
}
class PopIdleProxy {
  -String idleName
  -ArrayList<String> funList
  -boolean isScheduleEmpty
  -PopIdle real
  +setFunName() String
  +getFunList() ArrayList<String>
  +adjustSchedule() void
  +writeAutograph() void
  -realize() void
}

```

### シーケンス図

```mermaid
sequenceDiagram
Main->>+PopIdleProxy:new
Note right of PopIdleProxy:new PopIdleProxy()

Main->>+PopIdleProxy:adjustSchedule()
PopIdleProxy-->>-Main: 
Main->>+PopIdleProxy:setFunName()
PopIdleProxy-->>-Main: 
Main->>+PopIdleProxy:getFunList()
PopIdleProxy-->>-Main:funList

loop latestFunList
Main->>+PopIdleProxy:writeAutograph()
PopIdleProxy->>+PopIdle:new
Note right of PopIdle:new PopIdle() ※初回だけインスタンス化
PopIdleProxy->>+PopIdle:writeAutograph()
PopIdle-->>-PopIdleProxy: 
PopIdleProxy-->>-Main: 
end
```

### サンプルコード

#### インターフェース
```java:Entertainer.java
package Proxy;

import java.util.ArrayList;

public interface Entertainer {
  // マネージャーがやること
  public abstract void setFunName(String name);
  public abstract ArrayList<String> getFunList();
  public abstract void adjustSchedule();

  // アイドルがやること
  public abstract void writeAutograph(String name);
}
```

---

#### マネージャー（代理人）
```java:PopIdleProxy.java
package Proxy;

import java.util.ArrayList;

public class PopIdleProxy implements Entertainer {
  private String idleName;
  private ArrayList<String> funList;
  private boolean isScheduleEmpty = true;
  private PopIdle real;

  public PopIdleProxy(String idleName, ArrayList<String> funList, boolean isScheduleEmpty) {
    this.idleName = idleName;
    this.funList = funList;
    this.isScheduleEmpty = isScheduleEmpty;
  }

  public void setFunName(String funName) {
    this.funList.add(funName);
  }

  public ArrayList<String> getFunList() {
    return this.funList;
  }

  public void adjustSchedule() {
    if(this.isScheduleEmpty) {
      System.out.println("代理人：スケジュール調整完了");
      if (this.real != null) {
        this.real.adjustSchedule();
      }
    } else{
      System.out.println("代理人：スケジュール再調整");
    }
  }

  public void writeAutograph(String name) {
    realize();
    real.writeAutograph(name);
  }

  private synchronized void realize() {
    if (this.real == null) {
      this.real = new PopIdle(this.idleName, this.funList);
    }
  }
}
```

---

#### アイドル（本人）

```java:PopIdle.java
package Proxy;

import java.util.ArrayList;

public class PopIdle implements Entertainer {
  private String idleName;
  private ArrayList<String> funList;

  public PopIdle(String idleName, ArrayList<String> funList) {
    this.idleName = idleName;
    this.funList = funList;
    orderPending();
  }

  public void setFunName(String funName) {}

  public ArrayList<String> getFunList() {
    return this.funList;
  }

  public void adjustSchedule() {
  }

  public void writeAutograph(String funName) {
    System.out.println(funName+"さんへ いつも応援ありがとうございます");
    System.out.println(this.idleName+"より");
  }

  // 順番待ちの時間
  private void orderPending() {
    for (int i = 0; i < 5; i++) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // TODO: handle exception
      }
      System.out.print(".");
    }
    System.out.println("----入場開始----");
  }
}
```

---

#### 実行用

```java:Main.java
package Proxy;

import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    String idleName = "アイ";
    ArrayList<String> funList = new ArrayList<String>();
    funList.add("ruby");

    Entertainer ai = new PopIdleProxy(idleName, funList, true);
    ai.adjustSchedule();
    ai.setFunName("aqua");

    ArrayList<String> latestFunList = new ArrayList<String>();
    latestFunList = ai.getFunList();

    for (String funName: latestFunList) {
      // ここだけ本人の仕事
      ai.writeAutograph(funName);
    }
  }
}
```

### 実行結果

```text
代理人：スケジュール調整完了
.....----入場開始----
rubyさんへ いつも応援ありがとうございます
アイより
aquaさんへ いつも応援ありがとうございます
アイより
```
※アイドルのサイン会を想定してます。

## 終わりに
- Proxyパターンはシーケンス図で見ると理解しやすかった。
- 「アイドルもサイン書く以外色々やることあるんじゃ！」ってことについてはノータッチで。

以上。