はじめに
Creational patternsについて説明します。
Instance化を扱うDesign patternです。
Dependency inversion principle
Dependency inversion principle(DIP)はCreational patternの理解に役立ちます。
そのため、DIPについて説明します。
High-level moduleがLow-level moduleを利用するケースを考えます。
Low-level moduleを変更した場合、High-level moduleに影響を与えます。
High-level moduleとLow-level moduleの依存関係を弱める方策を検討します。
Low-level moduleのinterfaceを導入します。
High-level moduleはinterfaceを利用します。Low-level moduleはinterfaceを実装します。
次の図を参照ください。
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を利用するケースを考えます。
IProductはinterfaceであるためinstance化することができません。
そのためIProductを実装します。実装をProductとします。
次の通りです。
UserはProductをどのようにinstance化すればいいのでしょうか。
UserはIProductのみに依存し、Productには依存したくありません。
Creational patternsはこの課題に対処する方法を与えます。
Factory Method
Productのinstance化をUserのsubclassに任せる方法をFactoryMethodといいます。
実装例を示します。
makeProductをFactoryMethodといいます。
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といいます。
複数Productのinstance化をFactory objectに委譲します。
Userは直接Factoryには依存したくないため、IFactory interfaceを導入します。
次の通りです。
Productの実装を切り替えたい場合は次のようにして対処します。
AbstractFactoryといいます。
実装例を示します。
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化します。
次の図を参照ください。
ProductはPartA / PartBをもちます。
BuilderはPartA / PartB / Productをinstance化します。
DirectorはBuilderをつかってProductを組み立てます。
UserはDirectorからProductのinstanceを取得します。
実装例を示します。
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といいます。
実装例を示します。
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
*/
}