LoginSignup
8
7

More than 5 years have passed since last update.

Kotlinでデザインパターン Factory Method編

Last updated at Posted at 2019-02-05

はじめに

本記事はJava言語で学ぶデザインパターン入門を参考にしながら
JavaではなくKotlinで実装してみようというものです。

Java言語で学ぶデザインパターン入門

調べてみる

Factory Methodがやりたいことは、次に記載する内容です。

  • インスタンスとして生成されるクラスをProduct、インスタンスを生成するクラスをFactoryとして定義し、
    Productのインスタンスを生成したい場合には、必ずFactoryでProductのインスタンスを生成する。
  • インスタンスの生成をFactoryに任せるようにして、
    利用者側がProductのインスタンス生成の具体的手順がわからなくても良いようにする。

実装してみる

次のコードのようにProduceを継承したiPhoneのインスタンスを生成するコードがあったとします
このコードはiPhoneクラスのコンストラクタを利用してインスタンスを生成しています。
またiPhoneのMACアドレスの重複確認もあわせて実施しているせいで見通しがとても悪くなっています。

    val products : MutableList<Product> = mutableListOf()

    val product1 : Product = iPhone("80-88-31-47-c9-0c")
    if (products.find { item -> item.mac == product1.mac } != null) {
        throw Exception("duplicated mac address")
    }
    products.add(product1)

    val product2 : Product = iPhone("13-54-01-93-16-bb")
    if (products.find { item -> item.mac == product2.mac } != null) {
        throw Exception("duplicated mac address")
    }
    products.add(product2)

    val product3 : Product = iPhone("b7-ea-3a-c3-b0-cb")
    if (products.find { item -> item.mac == product3.mac } != null) {
        throw Exception("duplicated mac address")
    }
    products.add(product3)

そこで次のコードのようにFactory Methodパターンを適応し、
factory.create()を呼び出すだけでインスタンスを生成できるようにしたいと思います。
このように実装できればインスタンス生成の具体的な処理は隠蔽化され見通しがとても良くなります。

    val factory : Factory = iPhoneFactory()
    val product1 : Product = factory.create("80-88-31-47-c9-0c")
    val product2 : Product = factory.create("13-54-01-93-16-bb")
    val product3 : Product = factory.create("b7-ea-3a-c3-b0-cb")

Product

はじめにインスタンスを生成する対象のProduct(抽象クラス)を定義します。
今回はMACアドレスを所持し、起動、再起動、シャットダウンを制御できるProductクラスを定義しました。

interface Product {
    val mac: String
    fun boot()
    fun reboot()
    fun shutdown()
}

iPhone

Product(抽象クラス)を継承したiPhone(具象クラス)を定義します。
iPhoneのメソッドにはあとで呼び出しが明確になるようにメッセージをPrintします。

class iPhone(mac : String) : Product {

    override val mac: String = mac

    init {
        println("MACアドレスが${mac}であるiPhoneを作成します。")
    }

    override fun boot() {
        println("MACアドレスが${mac}であるiPhoneのiOSを起動します。")
    }

    override fun reboot() {
        println("MACアドレスが${mac}であるiPhoneのiOSを再起動します。")
    }

    override fun shutdown() {
        println("MACアドレスが${mac}であるiPhoneのiOSをシャットダウンします。")
    }
}

Factory

Product(抽象クラス)を生成するFactory(抽象クラス)を定義します。
FactoryではTemplate Methodパターンを適応してインスタンス生成の手順のみ定義します。
手順のみ定義するので具体的な処理はサブクラスで定義します。

abstract class Factory {
    // Template Methodパターンを用いてインスタンス生成の手順を定義
    fun create(mac : String) : Product {
        val p : Product = createProduct(mac)
        registerProduct(p)
        return p
    }

    // 手順の具体的な処理はサブクラスで定義する。
    abstract protected fun createProduct(mac : String) : Product
    abstract protected fun registerProduct(product : Product)
}

iPhoneFactory

Factory(抽象クラス)を継承したiPhoneFactory(具象クラス)を定義します。
Factoryにてインスタンス生成の手順は定義されているのでその手順にあわせて具体的な処理を実装します。

class iPhoneFactory : Factory() {
    // 作成したiPhoneの一覧
    val iphones = mutableListOf<Product>()

    override fun createProduct(mac: String): Product {
        // 作成するiPhoneのMACアドレスが重複していないか確認する。
        if (iphones.find { item -> item.mac == mac } != null) {
            throw Exception("duplicated mac address")
        }

        return iPhone(mac)
    }

    override fun registerProduct(product: Product) {
        // 登録するiPhoneのMACアドレスが重複していないか確認する。
        if (iphones.find { item -> item.mac == product.mac } != null) {
            throw Exception("duplicated mac address")
        }

        iphones.add(product)
    }
}

Main

Factorクラスでインスタンスを生成し、Productのメソッドを呼び出してみます。

fun main(args: Array<String>) {
    val factory : Factory = iPhoneFactory()
    val product1 : Product = factory.create("80-88-31-47-c9-0c")
    val product2 : Product = factory.create("13-54-01-93-16-bb")
    val product3 : Product = factory.create("b7-ea-3a-c3-b0-cb")

    product1.apply {
        boot()
        reboot()
        shutdown()
    }

    product2.apply {
        boot()
        reboot()
        shutdown()
    }

    product3.apply {
        boot()
        reboot()
        shutdown()
    }
}

---------------------------------------------------------------
MACアドレスが80-88-31-47-c9-0cであるiPhoneを作成します。
MACアドレスが13-54-01-93-16-bbであるiPhoneを作成します。
MACアドレスがb7-ea-3a-c3-b0-cbであるiPhoneを作成します。
MACアドレスが80-88-31-47-c9-0cであるiPhoneiOSを起動します。
MACアドレスが80-88-31-47-c9-0cであるiPhoneiOSを再起動します。
MACアドレスが80-88-31-47-c9-0cであるiPhoneiOSをシャットダウンします。
MACアドレスが13-54-01-93-16-bbであるiPhoneiOSを起動します。
MACアドレスが13-54-01-93-16-bbであるiPhoneiOSを再起動します。
MACアドレスが13-54-01-93-16-bbであるiPhoneiOSをシャットダウンします。
MACアドレスがb7-ea-3a-c3-b0-cbであるiPhoneiOSを起動します。
MACアドレスがb7-ea-3a-c3-b0-cbであるiPhoneiOSを再起動します。
MACアドレスがb7-ea-3a-c3-b0-cbであるiPhoneiOSをシャットダウンします。

おわりに

Facotry Methodパターンを適応してFactoryに、
インスタンス生成の処理を任せることで次のようなメリットが得られる。

  • 呼び出し側のインスタンス生成処理の見通しが良くなる。
  • 呼び出し側がProductのサブクラスに依存しなくなるため、サブクラスの変更の影響を受けにくくなる
8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7