デザインパターン入門

Bridgeパターン

機能の階層と実装の階層を分ける.

どんな場合に使うのか

サブクラスを作ろうと思うとき,自分の意図が「機能の追加」であるのか,「実装の追加」であるのかを確認する必要がある.クラス階層が1つだと,機能のクラス階層と実装のクラス階層が混在して,クラス階層が複雑化してしまう.そこで,「機能のクラス階層」と「実装のクラス階層」を2つの独立したクラス階層に分ける.その橋渡しをするのがBridgeパターン.

クラス図

ポイント

分けておけば,拡張するのが楽になる.

  • 機能を追加したければ,機能のクラス階層にクラスを追加する.このとき,実装のクラス階層は全く修正する必要がない.しかも追加した機能はすべての実装で利用できる.例えばOS依存の部分を実装のクラス階層で表現しておけば,1つの機能追加ですべての実装に対応できる.

継承は固い結びつき,委譲は緩やかな結びつき

  • 継承はソースコードを書き換えない限り代えることはできない.->必要に応じてクラス間の関係を切り替えたい場合に継承は不適切.委譲ではインスタンスを作る段階で実装を切り替えることができる.

サンプルプログラム

// CountDisplay.cs
namespace chap09 {
  //機能のクラス階層
  public class CountDisplay : Display {
    public CountDisplay(DisplayImpl imple) : base(imple) { }
    public void multiDisplay(int times) {
      open();
      for (int i = 0; i < times; i++) {
        print();
      }
      close();
    }
  }
}

// Display.cs
namespace chap09 {
  //機能のクラス階層
  public class Display {
    private DisplayImpl impl;
    public Display(DisplayImpl impl) {
      this.impl = impl;
    }
    public void open() {
      impl.rawOpen();
    }
    public void print() {
      impl.rawPrint();
    }
    public void close() {
      impl.rawClose();
    }
    public void display() {
      open();
      print();
      close();
    }
  }
}

// DisplayImpl.cs
namespace chap09 {
  //実装のクラス階層
  public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
  }
}

// Program.cs
namespace chap09 {
  class Program {
    static void Main(string[] args) {
      Display d1 = new Display(new StringDisplayImpl("Hello,Japan"));
      Display d2 = new CountDisplay(new StringDisplayImpl("Hello,World"));
      CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello,Universe"));
      d1.display();
      d2.display();
      d3.display();
      d3.multiDisplay(5);
      System.Console.Read();
    }
  }
}

// StringDisplayImpl.cs
using System;
using System.Text;
namespace chap09 {
  //実装のクラス階層
  public class StringDisplayImpl : DisplayImpl {
    private String str;
    private int width;
    public StringDisplayImpl(String str) {
      this.str = str;
      width = str.Length;
    }
    public override void rawOpen() {
      printLine();
    }
    public override void rawPrint() {
      Console.WriteLine("|" + str + "|");
    }
    public override void rawClose() {
      printLine();
    }
    private void printLine() {
      Console.Write("+");
      for (int i = 0; i < width; i++) {
        Console.Write("-");
      }
      Console.WriteLine("+");
    }
  }
}

練習問題後

クラス図


// CharDisplayImpl.cs
namespace chap09 {
  class CharDisplayImpl :DisplayImpl{
    private char begin;
    private char target;
    private char end;
    public CharDisplayImpl(char begin,char target,char end) {
      this.begin = begin;
      this.target = target;
      this.end = end;
    }
    public override void rawOpen() {
      System.Console.Write(begin);
    }
    public override void rawPrint() {
      System.Console.Write(target);
    }

    public override void rawClose() {
      System.Console.WriteLine(end);
    }
  }
}

// CountDisplay.cs
namespace chap09 {
  //機能のクラス階層
  public class CountDisplay : Display {
    public CountDisplay(DisplayImpl imple) : base(imple) { }
    public void multiDisplay(int times) {
      open();
      for (int i = 0; i < times; i++) {
        print();
      }
      close();
    }
  }
}

// Display.cs
namespace chap09 {
  //機能のクラス階層
  public class Display {
    private DisplayImpl impl;
    public Display(DisplayImpl impl) {
      this.impl = impl;
    }
    public void open() {
      impl.rawOpen();
    }
    public void print() {
      impl.rawPrint();
    }
    public void close() {
      impl.rawClose();
    }
    public void display() {
      open();
      print();
      close();
    }
  }
}

// DisplayImpl.cs
namespace chap09 {
  //実装のクラス階層
  public abstract class DisplayImpl {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
  }
}

// IncreaseDisplay.cs
namespace chap09 {
  class IncreaseDisplay : CountDisplay {
    private int count;
    public IncreaseDisplay(DisplayImpl imple) : base(imple) { }
    public void increaseDisplay() {
      multiDisplay(count++);
    }
  }
}

// Program.cs
namespace chap09 {
  class Program {
    static void Main(string[] args) {
      Display d1 = new Display(new CharDisplayImpl('<','*','>'));
      Display d2 = new CountDisplay(new CharDisplayImpl('|', '#', '-'));
      CountDisplay d3 = new CountDisplay(new CharDisplayImpl('@', '$', '>'));
      RandomDisplay d4 = new RandomDisplay(new CharDisplayImpl('&', '!', '%'));
      IncreaseDisplay d5 = new IncreaseDisplay(new StringDisplayImpl("Hello,World!"));
      d1.display();
      d2.display();
      d3.display();
      d3.multiDisplay(5);
      d4.display();
      d4.ramdomDisplay(5);
      d5.increaseDisplay();
      d5.increaseDisplay();
      d5.increaseDisplay();
      d5.increaseDisplay();
      d5.increaseDisplay();
      System.Console.Read();
    }
  }
}

// RandomDisplay.cs
using System;
namespace chap09 {
  public class RandomDisplay : CountDisplay {
    public RandomDisplay(DisplayImpl impl) : base(impl) { }
    public void ramdomDisplay(int times) {
      int count = (new Random()).Next(0, times);
      multiDisplay(count);
    }
  }
}

// StringDisplayImpl.cs
using System;
using System.Text;
namespace chap09 {
  //実装のクラス階層
  public class StringDisplayImpl : DisplayImpl {
    private String str;
    private int width;
    public StringDisplayImpl(String str) {
      this.str = str;
      width = str.Length;
    }
    public override void rawOpen() {
      printLine();
    }
    public override void rawPrint() {
      Console.WriteLine("|" + str + "|");
    }
    public override void rawClose() {
      printLine();
    }
    private void printLine() {
      Console.Write("+");
      for (int i = 0; i < width; i++) {
        Console.Write("-");
      }
      Console.WriteLine("+");
    }
  }
}

// TextFileDisplayImple.cs
using System;
using System.IO;
using System.Text;
namespace chap09 {
  public class TextFileDisplayImple : DisplayImpl {
    private string path;
    private StreamReader sr = null;
    private StringBuilder buffer = new StringBuilder();
    public TextFileDisplayImple(string path) {
      this.path = path;
    }
    public override void rawOpen() {
      try {
        sr = new StreamReader(path);
        String line;
        while ((line = sr.ReadLine()) != null) {
          buffer.AppendLine(line);
        }
      } catch (FileNotFoundException) {
      }
    }
    public override void rawPrint() {
      Console.WriteLine(buffer);
    }
    public override void rawClose() {
    }
  }
}
デザインパターン入門

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

管理人/副管理人のみ編集できます