デザインパターン勉強会　第9回：Bridgeパターン

デザインパターン勉強会　第9回：Bridgeパターン
#はじめに

本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「[Java言語で学ぶデザインパターン入門] (http://www.hyuki.com/dp/)」をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。  

第1回：[Iteratorパターン](http://qiita.com/Nuits/items/800f080d44b52e1cae18)
第2回：[Adapterパターン](http://qiita.com/Nanakusajp/items/747d1913b1e8bea8d340)
第3回：[Template Methodパターン](http://qiita.com/_kyam/items/9b5722e6a9635af6e18b)
第4回：[Factory Methodパターン](http://qiita.com/ayayo/items/3eece04bbc4b7504179a)
第5回：[Singletonパターン](http://qiita.com/Keenag/items/cae53d55cb953562fee4)
第6回：[Prototypeパターン](http://qiita.com/aconit96/items/9235d30a07f508886c7e)
第7回：[Builderパターン](http://qiita.com/Uesk/items/8f12be304671e9905795)
第8回：[AbstractFactoryパターン](https://qiita.com/buru/items/bb3930dda1bdd2b5f70b)

---
#Bridgeパターンとは
Bridgeパターンとは、２つのクラス階層、「機能のクラス階層」と「実装のクラス階層」を分離した上で、橋を渡すように結びつけるパターンです。

#「機能のクラス階層」と「実装のクラス階層」
クラスの階層には、「機能のクラス階層」と「実装のクラス階層」の二つの意味があります。

・「機能のクラス階層」とは
あるクラスSomeClassに新しい機能を追加したBetterClassを作成するような場合です。
いわゆるクラスの継承による機能拡張を指します。

・「実装のクラス階層」とは
親クラス(またはインターフェース)で規定された抽象メソッドを、子クラスで実装するような場合を指します。
実装方法の切り替えを実現するための階層関係です。

#アンチパターンの例
機能と実装の階層を分離しない場合、以下のようなクラス階層となることが考えられます。
![Bridge3.png](https://qiita-image-store.s3.amazonaws.com/0/202345/9c8ec4fe-5e77-a779-d4a6-23b1fb3d5d1b.png)

①AbstractClassの内部処理ExecuteInnerProcを実装する２つのクラスを作成し、実装の切り替えを可能にしていました。(実装の階層)
②AbstractClassを拡張したAbstractBetterClass作成した場合、それを継承して内部処理を実装するクラスを新たに２つ作成する必要が生じてしまいます。(実装の階層 * 機能の階層)

このような構成では、クラスの数が機能と実装の各種類数の積となってしまいます。
機能の拡張や実装の種類の追加がこの先も発生すると考えた場合、クラス数が積算で増加してしまうこの構成は良くありません。

前述の通り、Bridgeパターンはこの「機能のクラス階層」と「実装のクラス階層」を分離した上で、橋を渡すように結びつけるパターンです。

#Bridgeパターンのサンプルプログラム
##クラス図
![Bridge.png](https://qiita-image-store.s3.amazonaws.com/0/202345/a7eb73c8-5bd2-37eb-0d06-b86f984ab677.png)

機能の階層
　Display
　　└CountDisplay
実装の階層
　DisplayImpl
　　└NormalDisplayImpl
　　└StarDisplayImpl
Display(機能の階層)がDisplayImpl(実装の階層)を集約している右向きの矢印が、両者を結びつける「橋」にあたります。

---
##各クラスの役割
| 名前               | 役割            　          |
|:------------------|:----------------------------|
| Display           |「表示する」クラス             |
| CountDisplay      |「指定回数だけ表示する」という機能を追加したクラス      |
| DisplayImpl       |「表示する」ために使用するメソッドのインターフェース    |
| NormalDisplayImpl |通常の表示を実装したクラス            |
| StarDisplayImpl   | 強調した表示を実装したクラス      　　|



---
##Displayクラス
Displayクラスは、「表示する」機能を提供するクラスです。
「表示する」ためのpublicなExecuteDisplayメソッドが定義されています。
ExecuteDisplayの中では、
・表示の前処理　Openメソッド
・表示処理そのもの　printメソッド
・表示の後処理　Closeメソッド
という3つのメソッドの呼び出し順を定義しています。
これらのメソッドの中身では、実際の処理がDisplayImplインターフェースを実装するクラスに委譲しています。

```
    public class Display
    {
        private DisplayImpl _displayImpl;

        public Display(DisplayImpl displayImpl)
        {
            _displayImpl = displayImpl;
        }

        protected void Open()
        {
            _displayImpl.RawOpen();
        }

        protected void Print()
        {
            _displayImpl.RawPrint();
        }

        protected void Close()
        {
            _displayImpl.RawClose();
        }

        public void ExecuteDisplay()
        {
            Open();
            Print();
            Close();
        }
    }
```

---
##DisplayImplインターフェース
DisplayImplインターフェースは、「表示する」ために必要な手順であるOpen、Print、Closeの各メソッドを宣言しています。

```
    public interface DisplayImpl
    {
        void RawOpen();
        void RawPrint();
        void RawClose();
    }
```

---
##NormalDisplayImplクラス
NormalDisplayImplクラスはDisplayImplインターフェースを実装します。
「文字列を枠線で囲う」という表示方法を実装しています。

```
    public class NormalDisplayImpl : DisplayImpl
    {
        private string _string;
        private int _width;
        
        public NormalDisplayImpl(string s)
        {
            _string = s;
            _width = System.Text.Encoding.GetEncoding("Shift_JIS").GetByteCount(s);
        }

        public void RawOpen()
        {
            PrintLine();
        }

        public void RawPrint()
        {
            Console.WriteLine("|" + _string + "|");
        }

        public void RawClose()
        {
            PrintLine();
        }

        private void PrintLine()
        {
            Console.Write("+");
            for( int i = 0; i < _width; i++)
            {
                Console.Write("-");
            }
            Console.WriteLine("+");
        }
    }

```

---
##StarDisplayImplクラス
NormalDisplayImplクラスはDisplayImplインターフェースを実装するもう一つのクラスです。
「文字列を星で囲う」という表示方法を実装しています。

```
    public class StarDisplayImpl : DisplayImpl
    {
        private string _string;
        private int _width;
        
        public StarDisplayImpl(string s)
        {
            _string = s;
            _width = System.Text.Encoding.GetEncoding("Shift_JIS").GetByteCount(s);
        }

        public void RawOpen()
        {
            PrintLine();
        }

        public void RawPrint()
        {
            Console.WriteLine("*" + _string + "*");
        }

        public void RawClose()
        {
            PrintLine();
        }

        private void PrintLine()
        {
            Console.Write("*");
            for( int i = 0; i < _width; i++)
            {
                Console.Write("*");
            }
            Console.WriteLine("*");
        }
    }
```

---
##Program
動作確認用のテストプログラムです。

・Displayの参照 d1,d2
Displayのコンストラクタに渡すDisplayImplの実装クラスを切り替えることにより、表示する機能の実装が切り替わります。
・Displayの参照 d3
CountDisplayは子クラスなので、親クラスであるDisplayとして使うことができます。
・CountDisplayの参照 d3,d4
拡張機能であるMultiDisplayを使うことができ、コンストラクタに渡すDisplayImplによる実装の切り替えもできます。

```
    class Program
    {
        static void Main(string[] args)
        {
            Display d1 = new Display(new NormalDisplayImpl("枠線で表示"));
            Display d2 = new Display(new StarDisplayImpl("星で表示"));
            Display d3 = new CountDisplay(new StarDisplayImpl("星で表示(子クラス)"));

            CountDisplay C1 = new CountDisplay(new NormalDisplayImpl("増幅して枠線で表示"));
            CountDisplay C2 = new CountDisplay(new StarDisplayImpl("増幅して星で表示"));

            d1.ExecuteDisplay();
            d2.ExecuteDisplay();
            d3.ExecuteDisplay();

            C1.MultiDisplay(5);
            C2.MultiDisplay(3);

            Console.ReadLine();
        }
    }

```

---
#実行結果
先のProgramを実行した結果が以下の通りです。
![Bridge2.png](https://qiita-image-store.s3.amazonaws.com/0/202345/8c16553f-6eea-277c-c0a5-6f9e7525950f.png)

---
# まとめ
* Bridgeパターンは機能の階層と実装の階層を分離し、クラス階層を簡素化するためのパターン
* 機能の上位クラスが、集約している実装の上位クラスに処理を委譲することによって、２つの階層を橋のように結びつける
* 今回の例では、機能クラスの生成時に実装クラスを渡している。(→DIパターン)

---