LoginSignup
0
0

【Gang of Four】Abstract Factory

Last updated at Posted at 2019-03-18

#Abstract Factory
目次

オブジェクト指向の基本的な思想としてオブジェクト間の結合度が低いほうが良い、といったものがあります。
javaではinterfaceを用いることで、具象クラスを使う側が意識する必要がないようにできます。

インターフェース

Screen.java
interface Screen {
    public void view();
}

具象クラス

TopScreen.java
public class TopScreen implements Screen {
    public void view() {
        System.out.println("トップ画面");
    }
}
LoginScreen.java
public class LoginScreen implements Screen {
    public void view() {
        System.out.println("ログイン画面");
    }
}

使う側

Client.java
public class Client {
    public void displayTopScreen() {
        Screen top = new TopScreen()
        top.view();
    }

    public void displayLoginScreen() {
        Screen login = new LoginScreen()
        login.view();
    }
}

一見なんの問題もないように見えますが、各クラスをインスタンス化する部分であるScreen top = new TopScreen()Screen login = new LoginScreen()において、どうしても具象クラスを意識する必要がでてきます。
これは結合度が低いほうが良いという思想に反することになるうえ、使用しているコンストラクタを修正すればインスタンス化している箇所をすべて修正しなければならなくなります。

これをfactoryクラスというインスタンスを生成してくれるクラスを作成することによって解決しますが、そのfactoryクラスも抽象クラスと具象クラスに分けることで、より柔軟なオブジェクト生成工場を作ろうというのが本パターンです。

##目的
互いに関連したり依存し合うオブジェクト群を、その具象クラスを明確にせずに生成するためのインタフェースを提供する。
##構成要素
・AbstractFactory AbstractProductクラスを生成するインターフェースを定義する
・ConcreteFactory ConcreteProductクラスを生成するインターフェースを定義する
・AbstractProduct 生成される部品の共通部分を抽出した抽象クラス
・ConcreteProduct 生成される部品
・Client 利用者

##実装
ではサンプルコードです。
車に必要な部品を製造する工場を実装し、下記のようなフローでディーラーへ車両を陳列します。

ディーラーがメーカーへ車両を要求 -> メーカーが工場へ車用部品を要求 -> 工場がメーカーへ各種部品を返却 -> メーカーが車を組み立ててディーラーへ返却 -> ディーラーに車両が陳列される。

###AbstractFactory AbstractProductクラスを生成するインターフェースを定義する
車用部品製造工場クラス

CarFactory.kt
package abstractfactory

open class CarFactory {

    open fun makeEngine(displacement: Int): Engine {
        return Engine(displacement)
    }

    open fun makeTire(position: Int): Tire {
        return Tire(position)
    }

    open fun makeSteering(weight: Int): Steering {
        return Steering(weight)
    }

}

###AbstractProduct 生成される部品の共通部分を抽出した抽象クラス
エンジンクラス

Engine.kt
package abstractfactory

open class Engine(displacement: Int) {
    open val type = "普通のエンジン"
    val displacement = displacement
}

タイヤクラス

Tire.kt
package abstractfactory

open class Tire(position: Int) {
    open val type = "普通のタイヤ"
    val position = position
}

ハンドルクラス

Steering.kt
package abstractfactory

class Steering(weight: Int) {
    val type = "普通のハンドル"
    val weight = weight
}

車クラス

Car.kt
package abstractfactory

open class Car(type: String, engine: Engine, tire: Tire, steering: Steering) {
    val type = type
    val engine = engine
    val tire = tire
    val steering = steering

    fun getConstitution(): String {
        return  "【車種】:" + type + "," +
                "【エンジン】:" + engine.type + "," +
                "【タイヤ】:" + tire.type + "," +
                "【ハンドル】:" + steering.type
    }
}

###Client 利用者
自動車メーカークラス

Maker.kt
package abstractfactory

class Maker {

    /**
     * 普通の車製造
     */
    fun getCar(): Car {
        return make("ファミリーカー", CarFactory())
    }

    /**
     * 製造
     */
    private fun make(type: String, factory: CarFactory): Car {
        val engine = factory.makeEngine(1000)
        val tire = factory.makeTire(1)
        val steering = factory.makeSteering(100)

        return Car(type, engine, tire, steering)
    }

}

自動車ディーラークラス

AutomobileDealer.kt
package abstractfactory

class AutomobileDealer {

    val cars = mutableListOf<Car>()

    init {
        openingUp()
    }

    /**
     * ディーラーにある車一覧
     */
    fun showCars() {
        cars.forEach {
            println(it.getConstitution())
        }
    }

    /**
     * 店舗開店
     */
    private fun openingUp() {
        val maker = Maker()
        cars.add(maker.getCar())
    }
}

###出力結果
AutomobileDealer#showCars()を呼び出せば、陳列されている車両(のスペック)が出力されます。

[output]
【車種】:ファミリーカー,【エンジン】:普通のエンジン,【タイヤ】:普通のタイヤ,【ハンドル】:普通のハンドル

以上で完成しましたが、突然社長が今までの製造ラインはそのままに、同様のラインでスーパーカー用部品も製造できる工場を作れといいだしました。

###ConcreteFactory ConcreteProductクラスを生成するインターフェースを定義する
スーパーカー用部品製造工場クラス

SuperCarFactory.kt
package abstractfactory

class SuperCarFactory: CarFactory() {

    override fun makeEngine(displacement: Int): Engine {
        return HiPerformanceEngine(displacement)
    }

    override fun makeTire(position: Int): Tire {
        return HiGripTire(position)
    }
}

###ConcreteProduct 生成される部品
高出力エンジンクラス

HiPerformanceEngine.kt
package abstractfactory

class HiPerformanceEngine(displacement: Int): Engine(displacement) {
    override val type = "高出力エンジン"
}

ハイグリップタイヤクラス

HiGripTire.kt
package abstractfactory

class HiGripTire(position: Int): Tire(position) {
    override val type = "ハイグリップタイヤ"
}

メーカーにスーパーカー製造メソッドを追加します。

###Client 利用者
メーカークラス

Maker.kt
package abstractfactory

class Maker {

    /**
     * 車製造
     */
    fun getCar(): Car {
        return make(CarFactory())
    }

    /**
     * スーパーカー製造
     */
    fun getSuperCar(): Car {
        return make("スーパーカー", SuperCarFactory())
    }

    /**
     * 製造
     */
    private fun make(type: String, factory: CarFactory): Car {
        val engine = factory.makeEngine(1000)
        val tire = factory.makeTire(1)
        val steering = factory.makeSteering(100)

        return Car(type, engine, tire, steering)
    }

}

ディーラーは新たな車両(スーパーカー)をメーカーに要求します。

AutomobileDealer.kt
package abstractfactory

class AutomobileDealer {

    val cars = mutableListOf<Car>()

    init {
        openingUp()
    }

    /**
     * ディーラーにある車一覧
     */
    fun showCars() {
        cars.forEach {
            println(it.getConstitution())
        }
    }

    /**
     * 店舗開店
     */
    private fun openingUp() {
        val maker = Maker()
        cars.add(maker.getCar())
        cars.add(maker.getSuperCar())
        cars.add(maker.getSuperCar())
    }
}

AutomobileDealer#showCars()を呼び出せば、スーパーカーが新たに陳列されていることがわかります。

###出力結果

[output]
【車種】:ファミリーカー,【エンジン】:普通のエンジン,【タイヤ】:普通のタイヤ,【ハンドル】:普通のハンドル
【車種】:スーパーカー,【エンジン】:高出力エンジン,【タイヤ】:ハイグリップタイヤ,【ハンドル】:普通のハンドル
【車種】:スーパーカー,【エンジン】:高出力エンジン,【タイヤ】:ハイグリップタイヤ,【ハンドル】:普通のハンドル

以上で社長の要求に応じることができました。

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