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


Bridge - 橋

目次
通常、抽出できる部分を抽象クラスとし、それ以外の部分を抽象クラスを継承した具象クラスで実装します。

抽象クラスと具象クラスを分離し抽象クラスの実装を実行時に決定できるようにするのが本パターンです。

アイコンに関する情報を管理するアイコンクラスを例とします。
最初にモノクロアイコンクラス(抽象クラス)とモノクロアイコンを様々な大きさで表示させるための大モノクロアイコンクラス(具象クラス)中モノクロアイコンクラス(具象クラス)小モノクロアイコンクラス(具象クラス)を実装します。
しかしクライアントからの予定外の仕様変更により、途中でRGBを自由に設定できるカラーアイコンクラス(抽象クラス)を実装しなければならなくなりました。もちろんアイコンはモノクロアイコンと同様大、中、小の3種類を表示する仕様も満たしていなければいけません。

抽象クラスと具象クラスが継承により永続的に結合されているとカラーアイコンクラスを継承した大カラーアイコンクラス中カラーアイコンクラスを…略

といったようにカラーアイコンクラスを継承した全ての大~小アイコンを作成しなければならなくなります。さらにクリアアイコンを追加するときはまた同様に…略

DBで多対多のリレーショナルがあるテーブルを、中間テーブルを作成して正規化するのに近い考え方かな?

目的

抽出されたクラスと実装を分離して、それらを独立に変更できるようにする。

構成要素

・Abstraction 抽象クラス
・RefinedAbstraction Abstractionクラスを拡張したクラス
・Implementor 具象クラス
・ConcreteImplementor Implementorクラスを拡張したクラス

実装

まず抽象クラスのモノクロアイコンクラスとカラーアイコンクラスを実装します。

Abstraction 抽象クラス

抽象アイコンクラス
このクラスが所謂本パターンの肝、抽象クラスと具象クラスのになります。

AbstIcon.kt
package bridge

abstract class AbstIcon(iconType: IconType) {
    enum class IconType(val value: String) {
        BlackAndWhite("モノクロアイコン"),
        Color("カラーアイコン")
    }

    private var bigIcon: ImpIcon = BigIcon(iconType)
    private var middleIcon: ImpIcon = MiddleIcon(iconType)
    private var smallIcon: ImpIcon = SmallIcon(iconType)

    abstract fun getType(): String

    fun getBigIcon(): ImpIcon {
        return bigIcon
    }

    fun getMiddleIcon(): ImpIcon {
        return middleIcon
    }

    fun getSmallIcon(): ImpIcon {
        return smallIcon
    }
}

RefinedAbstraction Abstractionクラスを拡張したクラス

モノクロアイコン抽象クラス

AbstBlackAndWhiteIcon.kt
package bridge

class AbstBlackAndWhiteIcon: AbstIcon(IconType.BlackAndWhite) {
    override fun getType(): String {
        return AbstIcon.IconType.BlackAndWhite.value
    }

    // モノクロアイコン独自処理色々
}

カラーアイコン抽象クラス

AbstColorIcon.kt
package bridge

class AbstColorIcon: AbstIcon(IconType.Color) {

    override fun getType(): String {
        return AbstIcon.IconType.Color.value
    }

    // カラーアイコン独自処理色々
}

続いて具象クラスである大アイコン、中アイコン、小アイコンの作成

Implementor 具象クラス

具象アイコンインターフェース

ImpIcon.kt
package bridge

interface ImpIcon {
    enum class IconSize(val value: String) {
        Big("大アイコン"),
        Middle("中アイコン"),
        Small("小アイコン")
    }

    fun getIcon(): String
}

ConcreteImplementor Implementorクラスを拡張したクラス

大、中、小アイコンクラス

BigIcon.kt
package bridge

class BigIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Big.value
    }

}
MiddleIcon.kt
package bridge

class MiddleIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Middle.value
    }
}
SmallIcon.kt
package bridge

class SmallIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Small.value
    }
}

各アイコンを使う人

Client.kt
package bridge

class Client {
    init {
        val colorIcon = AbstColorIcon()
        println(colorIcon.getType())
        println(colorIcon.getBigIcon().getIcon())
        println(colorIcon.getMiddleIcon().getIcon())
        println(colorIcon.getSmallIcon().getIcon())

        val blackAndWhiteIcon = AbstBlackAndWhiteIcon()
        println(blackAndWhiteIcon.getType())
        println(blackAndWhiteIcon.getBigIcon().getIcon())
        println(blackAndWhiteIcon.getMiddleIcon().getIcon())
        println(blackAndWhiteIcon.getSmallIcon().getIcon())
    }
}

以上でアイコンを用いたサンプルコードの実装が完了です。

出力結果

[output]
カラーアイコン
【タイプ】:カラーアイコン【サイズ】:大アイコン
【タイプ】:カラーアイコン【サイズ】:中アイコン
【タイプ】:カラーアイコン【サイズ】:小アイコン
モノクロアイコン
【タイプ】:モノクロアイコン【サイズ】:大アイコン
【タイプ】:モノクロアイコン【サイズ】:中アイコン
【タイプ】:モノクロアイコン【サイズ】:小アイコン

ここで背景が透明なクリアアイコンを追加してみます。従来の方法では大クリアアイ…略ですが、本パターンではAbstIconクラスを継承したAbstClearIconクラスを実装するだけで実現できるようになっています。

AbstClearIcon.kt
package bridge

class AbstClearIcon: AbstIcon(IconType.Clear) {

    override fun getType(): String {
        return AbstIcon.IconType.Clear.value
    }

    // クリアアイコン独自処理色々
}

Clientクラスに下記コードを追加したあと再び実行してみます。

Client.kt
    val clearIcon = AbstClearIcon()
    println(clearIcon.getType())
    println(clearIcon.getBigIcon().getIcon())
    println(clearIcon.getMiddleIcon().getIcon())
    println(clearIcon.getSmallIcon().getIcon())
[output]
カラーアイコン
【タイプ】:カラーアイコン【サイズ】:大アイコン
【タイプ】:カラーアイコン【サイズ】:中アイコン
【タイプ】:カラーアイコン【サイズ】:小アイコン
モノクロアイコン
【タイプ】:モノクロアイコン【サイズ】:大アイコン
【タイプ】:モノクロアイコン【サイズ】:中アイコン
【タイプ】:モノクロアイコン【サイズ】:小アイコン
クリアアイコン
【タイプ】:クリアアイコン【サイズ】:大アイコン
【タイプ】:クリアアイコン【サイズ】:中アイコン
【タイプ】:クリアアイコン【サイズ】:小アイコン

簡単に拡張することができました。

これで抽象クラス側がいくら増えても、具象クラス側がいくら増えても用意に実装することが可能になりました。