【Gang of Four】Visitor

#Visitor
**[目次](https://qiita.com/Tonkachi0620/items/03986742efbb7e3a9bbe)**
訪問者に全ての要素に対するインターフェースを用意しておき、その要素に関する処理を訪問者にやってもらうというパターンだと理解しています

##目的
あるオブジェクト構造上の要素で実行されるオペレーションを表現する。Visitor パターンにより、オペレーションを加えるオブジェクトのクラスに変更を加えずに、新しいオペレーションを定義することができるようになる

##構成要素
・Visitor 訪問者抽象クラス
・ConcreteVisitor 訪問者具象クラス
・Element 訪問者に処理を移譲する要素の抽象クラス
・ConcreteElement 訪問者に処理を移譲する要素の具象クラス
・ObjectStructure 要素を列挙することができるクラス（らしいんですが必要性を理解できていません）

##実装
用意された材料を使って料理を作るプログラムを実装します。

###Visitor 訪問者抽象クラス

```Visitor.kt
package visitor

interface Visitor {
    fun visit(e: PotatoElement)
    fun visit(e: CarrotElement)
    fun visit(e: PorkElement)
    fun visit(e: OtherElement)
    fun serve()
}
```

###ConcreteVisitor 訪問者具象クラス

カレーを作る訪問者

```CurryVisitor.kt
package visitor

class CurryVisitor: Visitor {
    private var cookingPotato = false
    private var cookingCarrot = false
    private var cookingPork = false
    private var cookingOther = false

    override fun visit(e: PotatoElement) {
        cookingPotato = true
        println("${e.name()}の皮を剥いて切って鍋で煮込みます。")
    }

    override fun visit(e: CarrotElement) {
        cookingCarrot = true
        println("${e.name()}の芽をとって皮を剥いて切って鍋で煮込みます。")
    }

    override fun visit(e: PorkElement) {
        cookingPork = true
        println("${e.name()}を切って鍋で煮込みます。")
    }

    override fun visit(e: OtherElement) {
        cookingOther = true
        println("カレーのルーと${e.name()}を鍋に入れて煮込みます。")
    }

    override fun serve() {
        if (cookingPotato && cookingCarrot && cookingOther && cookingPork) {
            println("カレーの完成！")
        } else {
            println("まだ料理中です。")
        }
    }

}
```

###Element 訪問者に処理を移譲する要素の抽象クラス
材料抽象クラス

```Element.kt
package visitor

interface Element {
    fun name(): String
    fun cooking(v: Visitor)
}
```

###ConcreteElement 訪問者に処理を移譲する要素の具象クラス
材料具象クラスたち
じゃがいも

```PotatoElement.kt
package visitor

class PotatoElement: Element {
    override fun name(): String {
        return "じゃがいも"
    }

    override fun cooking(v: Visitor) {
        v.visit(this)
    }
}
```

にんじん

```CarrotElement.kt
package visitor

class CarrotElement: Element {
    override fun name(): String {
        return "にんじん"
    }

    override fun cooking(v: Visitor) {
        v.visit(this)
    }
}
```

豚肉

```PorkElement.kt
package visitor

class PorkElement: Element {
    override fun name(): String {
        return "豚肉"
    }

    override fun cooking(v: Visitor) {
        v.visit(this)
    }

}
```

その他

```OtherElement.kt
package visitor

class OtherElement: Element {
    override fun name(): String {
        return "塩と胡椒"
    }

    override fun cooking(v: Visitor) {
        v.visit(this)
    }
}
```

###使う人

```Client.kt
package visitor

class Client {
    init {
        serveCurry()
    }

    private fun serveCurry() {
        val pot = PotatoElement()
        val c = CarrotElement()
        val por = PorkElement()
        val o = OtherElement()
        val v = CurryVisitor()

        pot.cooking(v)
        c.cooking(v)
        por.cooking(v)
        v.serve()
        o.cooking(v)
        v.serve()
    }
}
```

```
[out-put]

じゃがいもの皮を剥いて切って鍋で煮込みます。
にんじんの芽をとって皮を剥いて切って鍋で煮込みます。
豚肉を切って鍋で煮込みます。
まだ料理中です。
カレーのルーと塩と胡椒を鍋に入れて煮込みます。
カレーの完成！
```

野菜炒めを作りたくなったのでFriedVegetablesVisitorを実装します。

```FriedVegetablesVisitor.kt
package visitor

class FriedVegetablesVisitor: Visitor {
    private var cookingPotato = false
    private var cookingCarrot = false
    private var cookingPork = false
    private var cookingOther = false

    override fun visit(e: PotatoElement) {
        cookingPotato = true
        println("${e.name()}の皮を剥いて切って炒めます。")
    }

    override fun visit(e: CarrotElement) {
        cookingCarrot = true
        println("${e.name()}の芽をとって皮を剥いて切って炒めます。")
    }

    override fun visit(e: PorkElement) {
        cookingPork = true
        println("${e.name()}を切って炒めます。")
    }

    override fun visit(e: OtherElement) {
        cookingOther = true
        println("${e.name()}を鍋に入れて炒めます。")
    }

    override fun serve() {
        if (cookingPotato && cookingCarrot && cookingOther && cookingPork) {
            println("野菜炒めの完成！")
        } else {
            println("まだ料理中です。")
        }
    }
}
```

```Client.kt
package visitor

class Client {
    init {
        serveCurry()
        println("--------------------------------")
        serveFriedVegetables()
    }

    private fun serveFriedVegetables() {
        val pot = PotatoElement()
        val c = CarrotElement()
        val por = PorkElement()
        val o = OtherElement()
        val v = FriedVegetablesVisitor()

        pot.cooking(v)
        c.cooking(v)
        por.cooking(v)
        v.serve()
        o.cooking(v)
        v.serve()
    }

    private fun serveCurry() {
        val pot = PotatoElement()
        val c = CarrotElement()
        val por = PorkElement()
        val o = OtherElement()
        val v = CurryVisitor()

        pot.cooking(v)
        c.cooking(v)
        por.cooking(v)
        v.serve()
        o.cooking(v)
        v.serve()
    }
}
```

###出力結果

```
[out-put]
じゃがいもの皮を剥いて切って鍋で煮込みます。
にんじんの芽をとって皮を剥いて切って鍋で煮込みます。
豚肉を切って鍋で煮込みます。
まだ料理中です。
カレーのルーと塩と胡椒を鍋に入れて煮込みます。
カレーの完成！
--------------------------------
じゃがいもの皮を剥いて切って炒めます。
にんじんの芽をとって皮を剥いて切って炒めます。
豚肉を切って炒めます。
まだ料理中です。
塩と胡椒を鍋に入れて炒めます。
野菜炒めの完成！
```