LoginSignup
1
1

More than 3 years have passed since last update.

Design pattern - Creational patternsを理解する

Last updated at Posted at 2021-01-30

はじめに

Creational patternsについて説明します。
Instance化を扱うDesign patternです。

Dependency inversion principle

Dependency inversion principle(DIP)はCreational patternの理解に役立ちます。
そのため、DIPについて説明します。

High-level moduleがLow-level moduleを利用するケースを考えます。

1.jpg

Low-level moduleを変更した場合、High-level moduleに影響を与えます。
High-level moduleとLow-level moduleの依存関係を弱める方策を検討します。

Low-level moduleのinterfaceを導入します。
High-level moduleはinterfaceを利用します。Low-level moduleはinterfaceを実装します。
次の図を参照ください。

1.jpg

interfaceにより依存関係が弱まります。

DIPは次のように表現されます。

High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces).
Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

  • High-level moduleはLow-level moduleに依存するのではなく、ともにInterface(*1)に依存すべき。
  • 実装はInterface依存すべき。

(*1) : ここの例ではLow-level moduleのinterface
(*2) : ここの例ではLow-level moduleの実装

Instance化

User classがIProduct interfaceを利用するケースを考えます。

1.jpg

IProductはinterfaceであるためinstance化することができません。
そのためIProductを実装します。実装をProductとします。
次の通りです。

1.jpg

UserはProductをどのようにinstance化すればいいのでしょうか。
UserはIProductのみに依存し、Productには依存したくありません。

Creational patternsはこの課題に対処する方法を与えます。

Factory Method

Productのinstance化をUserのsubclassに任せる方法をFactoryMethodといいます。

1.jpg

実装例を示します。
makeProductをFactoryMethodといいます。

test.kt
interface IProduct {
    fun method()
}

class Product : IProduct {
    override fun method() {
        println("Product:method")
    }
}

abstract class User {
    protected abstract fun makeProduct(): IProduct

    fun method() {
        val product = makeProduct()
        println("call IProduct:method")
        product.method()
    }
}

class UserImpl : User() {
    override fun makeProduct(): IProduct {
        return Product()
    }
}

fun callMethod(user: User) {
    println("call User:method")
    user.method()
}

fun main() {
    val user = UserImpl()
    callMethod(user)
    /*
        call User:method
        call IProduct:method
        Product:method
     */
}

Factory Object / Abstract Factory

Productのinstance化をObjectに委譲します。
ここでは便宜的にFactoryObjectといいます。

1.jpg

複数Productのinstance化をFactory objectに委譲します。
Userは直接Factoryには依存したくないため、IFactory interfaceを導入します。
次の通りです。

1.jpg

Productの実装を切り替えたい場合は次のようにして対処します。
AbstractFactoryといいます。

1.jpg

実装例を示します。

test.kt
interface IProductA {
    fun method()
}

class Product1A : IProductA {
    override fun method() {
        println("Product1A:method")
    }
}

class Product2A : IProductA {
    override fun method() {
        println("Product2A:method")
    }
}

interface IProductB {
    fun method()
}

class Product1B : IProductB {
    override fun method() {
        println("Product1B:method")
    }
}

class Product2B : IProductB {
    override fun method() {
        println("Product2B:method")
    }
}

interface IFactory {
    fun makeProductA(): IProductA
    fun makeProductB(): IProductB
}

class Factory1 : IFactory {
    override fun makeProductA(): IProductA {
        return Product1A()
    }

    override fun makeProductB(): IProductB {
        return Product1B()
    }
}

class Factory2 : IFactory {
    override fun makeProductA(): IProductA {
        return Product2A()
    }

    override fun makeProductB(): IProductB {
        return Product2B()
    }
}

class User(private val factory: IFactory) {
    fun method() {
        val productA = factory.makeProductA()
        val productB = factory.makeProductB()
        println("Call IProductA:method")
        productA.method()
        println("Call IProductB:method")
        productB.method()
    }
}

fun main() {
    User(Factory1()).method()
    User(Factory2()).method()
    /*
        Call IProductA:method
        Product1A:method
        Call IProductB:method
        Product1B:method
        Call IProductA:method
        Product2A:method
        Call IProductB:method
        Product2B:method
     */
}

Builder

複数objectの合成によって生成されるobjectをcomplex objectといいます。
複数objectを生成して、組み立て(assemble)ることでcomplex objectをinstance化します。
次の図を参照ください。

1.jpg

ProductはPartA / PartBをもちます。
BuilderはPartA / PartB / Productをinstance化します。
DirectorはBuilderをつかってProductを組み立てます。
UserはDirectorからProductのinstanceを取得します。

実装例を示します。

test.kt
interface IProduct {
    fun method()
}

class Product(private val partA: PartA, private val partB: PartB) : IProduct {
    override fun method() {
        println("call PartA:method")
        partA.method()
        println("call PartB:method")
        partB.method()
    }
}

class PartA {
    fun method() {
        println("PartA:method")
    }
}

class PartB {
    fun method() {
        println("PartB:method")
    }
}

interface IBuilder {
    fun makePartA(): PartA
    fun makePartB(): PartB
    fun makeProduct(partA: PartA, partB: PartB): IProduct
}

class Builder() : IBuilder {
    override fun makePartA(): PartA {
        return PartA()
    }

    override fun makePartB(): PartB {
        return PartB()
    }

    override fun makeProduct(partA: PartA, partB: PartB): IProduct {
        return Product(partA, partB)
    }
}

interface IDirector {
    fun makeProduct(): IProduct
}

class Director(private val builder: IBuilder) : IDirector {
    override fun makeProduct(): IProduct {
        val partA = builder.makePartA()
        val partB = builder.makePartB()
        return builder.makeProduct(partA, partB)
    }
}

class User() {
    fun method() {
        val builder = Builder()
        val director = Director(builder)
        val product: IProduct = director.makeProduct()
        println("call IProduct:method")
        product.method()
    }
}

fun main() {
    User().method()
    /*
        call IProduct:method
        call PartA:method
        PartA:method
        call PartB:method
        PartB:method
     */
}

Injection

User / IProductの外部にあるInjectorがProductをinstance化します。
InjectorはProductをUserに渡します。渡すことをinjectionといいます。

1.jpg

実装例を示します。

test.kt
interface IProduct {
    fun method()
}

class Product : IProduct {
    override fun method() {
        println("Product:method")
    }
}

class User(private val product: IProduct) {
    fun method() {
        println("call IProduct:method")
        product.method()
    }
}

class Injector {
    fun method() {
        User(Product()).method()
    }
}

fun main() {
    Injector().method()
    /*
        call IProduct:method
        Product:method
     */
}

References

1
1
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
1
1