「HeadFirstデザインパターン」と「Rubyによるデザインパターン」を読んで Composite パターン

何番煎じか判りませんがお勉強メモを残します

「[HeadFirstデザインパターン](https://www.amazon.co.jp/dp/4873112494)」第9章
「[Rubyによるデザインパターン](https://www.amazon.co.jp/dp/4894712857)」第6章

# Composite パターン

#### 「HeadFirstデザインパターン」でのJavaコードは（だいたい）こんな感じ

- パンケーキメニュー
- 食堂メニュー
- カフェメニュー

を表示します。現状はこんな感じです

```java
Iterator pancakeIterator = getPancakeIterator();
Iterator dinerIterator   = getDinerIterator();
Iterator cafeIterator    = getCafeIterator();

System.out.println("パンケーキメニュー");
printMenu(pancakeIterator);

System.out.println("食堂メニュー");
printMenu(dinerIterator);

System.out.println("カフェメニュー");
printMenu(cafeIterator);
```

メニューが増えるたびにソースをコピペしないとイケない感がすごい
これは普通どうにかしたくなる

```java
public void printMenus(menus) {
  while(menus.hasNext()) {
    Iterator menu = menus.next();
    printMenu(menu);
  }
}

ArrayList menus = new ArrayList();

menus.add(getPancakeIterator());
menus.add(getDinerIterator());
menus.add(getCafeIterator());

printMenus(menus)
```

誰だってそーする おれもそーする

が、上司に
**「食堂メニューの下にサブメニューとしてデザートメニューを持つ事になった。チャッチャと修正よろしく」**
と言われた時の衝撃は皆さま経験があるかと思います

いまこそ Composite パターン の出番だ！

部分-全体構造を表現する為にオブジェクトをツリー構造に構成できる
子要素を持つ要素をノードと呼び、子要素を持たない要素をリーフ と呼んだりする
ノードとリーフ、両方に対して同じ操作を適用できる 両者の違いを無視できる

コンポジション？コンポーネント？コンポジット？
何だか判らないと思うので「ツリー構造のアレ」くらいに思っていればいいと思います

```java
public abstract class MenuComponent {
  // 「コンポジット」メソッド
  public void add(MenuComponent menuComponent) {
    throw new NoMethodError();
  }
  public void delete(MenuComponent menuComponent) {
    throw new NoMethodError();
  }
  public MenuComponent getChild(int i) {
    throw new NoMethodError();
  }

  // 「操作」メソッド
  public String getName() {
    throw new NoMethodError();
  }
  public String getDescription() {
    throw new NoMethodError();
  }
  public double getPrice() {
    throw new NoMethodError();
  }

  // Menu, ItemMenu 両方が実装する「操作」メソッド
  public void print() {
    throw new NoMethodError();
  }
}
```

```java
// ノード
public class Menu extends MenuComponent {
  ArrayList menuComponents = new ArrayList();
  String name;
  String description;

  public Menu(String menu, String description) {
    this.name        = name;
    this.description = description;
  }

  public void add(MenuComponent menuComponent) {
    menuComponents.add(menuComponent);
  }
  public void delete(MenuComponent menuComponent) {
    menuComponents.remove(menuComponent);
  }
  public MenuComponent getChild(int i) {
    return (MenuComponent)menuComponents.get(i);
  }

  public String getName() {
    return name;
  }
  public String getDescription() {
    return description;
  }

  public void print() {
    System.out.println("");
    System.out.println(getName());
    System.out.println(getDescription());
    System.out.println("↓サブメニュー");

    Iterator iterator = menuComponents.iterator();

    while (iterator.hasNext()) {
      MenuComponent menuComponent = (MenuComponent)iterator.next();
      menuComponent.print();
    }
  }
}
```

```java
// リーフ
public class MenuItem extends MenuComponent {
  String name;
  String description;
  double price;

  public MenuItem(String menu, String description, double price) {
    this.name        = name;
    this.description = description;
    this.price       = price;
  }

  public String getName() {
    return name;
  }
  public String getDescription() {
    return description;
  }
  public double getPrice() {
    return price;
  }

  public void print() {
    System.out.println("");
    System.out.println(getName());
    System.out.println(getDescription());
    System.out.println(getPrice());
  }
}
```

```java
MenuComponent allMenus = new Menu("全メニュー", "すべてを統合したメニュー");

MenuComponent pancakeMenu = new Menu("パンケーキメニュー", "パンケーキ説明");
MenuComponent dinerMenu   = new Menu("食堂メニュー", "食堂説明");
MenuComponent CafeMenu    = new Menu("カフェメニュー", "カフェ説明");
MenuComponent dessertMenu = new Menu("デザートメニュー", "デザート説明");

pancakeMenu.add(new MenuItem("パンケーキメニュー１", "説明", 1.1))
dinerMenu.add(new MenuItem("食堂メニュー１", "説明", 2.1))
CafeMenu.add(new MenuItem("カフェメニュー１", "説明", 3.1))
dessertMenu.add(new MenuItem("デザートメニュー１", "説明", 4.1))

allMenus.add(pancakeMenu);
allMenus.add(dinerMenu);
allMenus.add(CafeMenu);

dinerMenu.add(dessertMenu); // 孫Menuになる

allMenus.print();
// ""
// "全メニュー"
// "すべてを統合したメニュー"
// "↓サブメニュー"
// ""
// "パンケーキメニュー"
// "パンケーキ説明"
// "↓サブメニュー"
// ""
// "パンケーキメニュー１"
// "説明"
// "1.1"
// ""
// "食堂メニュー"
// "食堂説明"
// "↓サブメニュー"
// ""
// "デザートメニュー"
// "デザート説明"
// ""
// "デザートメニュー１"
// "説明"
// "4.1"
// ""
// "食堂メニュー１"
// "説明"
// "2.1"
// ""
// "カフェメニュー"
// "カフェ説明"
// ""
// "カフェメニュー１"
// "説明"
// "3.1"
```

ノードとリーフ、区別できない（しなくてもよい）ようにするものだと上に書きましたが
クライアントからはそう見えるだけで、ノードとリーフは結局のところ別クラスです
だからこそ今回の例では
Menu（ノード）にはpriceは設定できない（必要がない）し、
MenuItem（リーフ）には小要素は追加できない（必要がない）
のですし、誤った操作をしてしまう心配がないとも言えます

あと、子要素を取得している箇所は省略しています
自分がノードなら「子要素を反復処理するIterator」
自分がリーフなら「has_next()が必ずfalseを返すIterator」
を使用する感じです

#### ~~Rubyではどうなるんや？~~

#### 「Rubyによるデザインパターン」でのコードは（だいたい）こんな感じ

ケーキを作る手順を細分化する
大きく分けて ケーキを焼く手順・パッケージする手順 等に分かれている
ケーキを焼く手順は バターを作る手順・焼く手順 等に分かれている
つまり、ケーキを焼く手順はツリー構造で考える事ができる
てきとーに細分化したのが下図である（書籍よりてきとーに減らしました）

```txt
- Manufacture Cake
  - Make Cake
    - Make Batter
    - AddDryIngredientsTask
    - Bake
  - Package Cake
    - Box
```

ひとつひとつの手順を個々のクラスにモデル化します
すべての手順に、作業時間を報告するメソッド(time_required)を実装します
ノードのtime_requiredは、自分の子要素のtime_requiredを合計します（これがミソです）
この段では "Make Cake" のツリーを作ってみます

```ruby
class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def time_required
    0.0
  end
end
```

```ruby
class MakeBatter < Task
  def initialize
    super('Make batter task')
  end

  def time_required
    1.0
  end
end

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients task')
  end

  def time_required
    2.0
  end
end

class BakeTask < Task
  def initialize
    super('Bake task')
  end

  def time_required
    3.0
  end
end
```

```ruby
class MakeCakeTask < Task
  def initialize
    super('Make cake task')
    @sub_tasks = []
    add_sub_task(MakeBatter.new) 
    add_sub_task(AddDryIngredientsTask.new) 
    add_sub_task(BakeTask.new)
  end

  def add_sub_task(task)
    @sub_tasks << task
  end

  def delete_sub_task(task)
    @sub_tasks.delete(task)
  end

  def time_required
    @sub_tasks.sum(&:time_required)
  end
end
```

```ruby
MakeCakeTask.new.time_required
# => 6.0

BakeTask.new.time_required
# => 3.0
```

"子task管理の処理は基底クラスに移したほうが良いでしょう" との事なので、その通りにします
ノードは Task を継承して
リーフは CompositeTask < Task を継承する
という事ですね 
"Manufacture Cake" からのすべてのツリーを実装してみます

```ruby
class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def time_required
    0.0
  end
end
```

```ruby
class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end

  def add_sub_task(task)
    @sub_tasks << task
  end

  def delete_sub_task(task)
    @sub_tasks.delete(task)
  end

  def time_required
    @sub_tasks.sum(&:time_required)
  end
end
```

```ruby
class MakeBatter < Task
  def initialize
    super('Make batter task')
  end

  def time_required
    1.0
  end
end

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients task')
  end

  def time_required
    2.0
  end
end

class BakeTask < Task
  def initialize
    super('Bake task')
  end

  def time_required
    3.0
  end
end

class Box < Task
  def initialize
    super('Bake task')
  end

  def time_required
    4.0
  end
end
```

```ruby
class MakeCakeTask < CompositeTask
  def initialize
    super('Make cake task')
    add_sub_task(AddDryIngredientsTask.new) 
    add_sub_task(BakeTask.new)
  end
end

class PackageCakeTask < CompositeTask
  def initialize
    super('Package cake task')
    add_sub_task(Box.new) 
  end
end
```

```ruby
class ManufactureCakeTask < CompositeTask
  def initialize
    super('Manufacture cake task')
    add_sub_task(MakeCakeTask.new) 
    add_sub_task(PackageCakeTask.new) 
  end
end
```

```ruby
ManufactureCakeTask.new.time_required
# => 10.0

MakeCakeTask.new.time_required
# => 6.0

BakeTask.new.time_required
# => 3.0
```

composite パターンは再帰的な性質を理解すれば思ったよりも単純
全体がグループとよく似ている場合、composite パターンがよく合う