【Gang of Four】デザインパターン学習 - Composite


Composite - 複合

目次
クラスを木構造で組み立てる。とあるように、compositeとleafと呼ばれるオブジェクトで構成するパターンのようです。

目的

部分―全体階層を表現するために、オブジェクトを木構造に組み立てる。Composite パターンにより、クライアントは、個々のオブジェクトとオブジェクトを合成したものを一様に扱うことができるようになる。

構成要素

・Component 根、節、葉の抽象クラス
・Leaf 葉
・Composite 根または節
・Client 使用者

実装

木構造と聞くとディレクトリツリーが思い浮かびますね。
ということでディレクトリツリーを管理、出力できるプログラムを実装します。

directoryがcompositeでfileがleafですね。

Component 根、節、葉の抽象クラス

directoryとfileのインターフェース

Element.kt
package composite

interface Element {
    enum class ElementType(val type: String) {
        DIRECTORY("Direcotry"),
        FILE("File")
    }

    fun getType(): ElementType
    fun getName(): String
}

Composite 根または節

directory抽象クラス

elementListを持たせ、自分の配下にいるElementを管理できるようにします。

AbstractDirectory.kt
package composite

abstract class AbstractDirectory: Element {
    var elementList: MutableList<Element> = mutableListOf()

    abstract fun addElement(element: Element)
}

directory具象クラス

Directory.kt
package composite

class Directory(private val name: String): AbstractDirectory() {

    override fun getType(): Element.ElementType {
        return Element.ElementType.DIRECTORY
    }

    override fun addElement(element: Element) {
        elementList.add(element)
    }

    override fun getName(): String {
        return name
    }
}

Leaf 葉

file具象クラス

File.kt
package composite

class File(private val name: String): Element {

    override fun getType(): Element.ElementType {
        return Element.ElementType.FILE
    }

    override fun getName(): String {
        return name
    }
}

Client 使用者

ディレクトリツリーを管理する人

DirectoryManager.kt
package composite

class DirectoryManager {
    init {
        // ルートフォルダ
        val rootDirectory = Directory("/")
        rootDirectory.addElement(File("sample.config"))
        rootDirectory.addElement(File("gof.env"))

        // iOSアプリ用フォルダ
        val iosDirectory = Directory("iOS")

        val iosAppDirectory = Directory("GofiOSApp")
        iosAppDirectory.addElement(File("xcode.project"))

        val iosAppSourceDirectory = Directory("Source")
        iosAppSourceDirectory.addElement(File("hoge.swift"))
        iosAppSourceDirectory.addElement(File("fuga.swift"))

        iosDirectory.addElement(iosAppDirectory)
        iosAppDirectory.addElement(iosAppSourceDirectory)
        rootDirectory.addElement(iosDirectory)

        // Androidアプリ用フォルダ
        val androidDirectory = Directory("AndroidOS")

        val androidAppDirectory = Directory("GofAndroidApp")
        androidAppDirectory.addElement(File("AndroidManifest.xml"))

        val androidAppSourceDirectory = Directory("Source")
        androidAppSourceDirectory.addElement(File("moge.kt"))
        androidAppSourceDirectory.addElement(File("unbaba.kt"))

        androidDirectory.addElement(androidAppDirectory)
        androidAppDirectory.addElement(androidAppSourceDirectory)
        rootDirectory.addElement(androidDirectory)

        show(rootDirectory, 0)
    }

    private fun show(element: Element, indent: Int) {

        if (element is Directory) {
            println("${"----".repeat(indent)}【${element.getType().type}】${element.getName()}")
            element.elementList.forEach {
                show(it, indent + 1)
            }
        } else {
            println("${"----".repeat(indent)}【${element.getType().type}】${element.getName()}")
        }
    }
}

以上でcompositeパターンを用いたディレクトリツリーの完成です。

出力結果

[output]
【Direcotry】/
----【File】sample.config
----【File】gof.env
----【Direcotry】iOS
--------【Direcotry】GofiOSApp
------------【File】xcode.project
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
----【Direcotry】AndroidOS
--------【Direcotry】GofAndroidApp
------------【File】AndroidManifest.xml
------------【Direcotry】Source
----------------【File】moge.kt
----------------【File】unbaba.kt

/iOS/GofIosApp/Sourceフォルダを同じ階層へ複製したくなった場合、iosAppDirectory.addElement(iosAppSourceDirectory)を追加します。

[output]
【Direcotry】/
----【File】sample.config
----【File】gof.env
----【Direcotry】iOS
--------【Direcotry】GofiOSApp
------------【File】xcode.project
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
略

ディレクトリを自由自在に操れてます。
※kotlinは文字列操作なんかも簡潔に書けていいですね。