導入
構造に関するパターンは「クラスの組み合わせ方」を工夫します。Adapterは「互換性のない既存クラスを、望む形に変換する変換プラグ」、Bridgeは「機能の階層と実装の階層を分離」して独立に拡張できるようにします。
図解
flowchart LR
C["利用側が期待する形<br/>ITarget"] --> AD["Adapter<br/>変換"]
AD --> OLD["既存の別インターフェース<br/>(そのままでは使えない)"]
style AD fill:#e1f5fe
サンプル
// Adapter: 既存の OldPrinter を、新しい IPrinter として使えるよう変換
IPrinter printer = new PrinterAdapter(new OldPrinter());
printer.Print("資料"); // 旧式印刷: 資料
// Bridge: 「図形(抽象)」と「描画方法(実装)」を分離して独立に組み合わせる
var redCircle = new Circle(new RedRenderer());
redCircle.Draw(); // 赤で 円 を描画
interface IPrinter { void Print(string doc); }
class OldPrinter { public void PrintOldStyle(string s) => Console.WriteLine($"旧式印刷: {s}"); }
class PrinterAdapter : IPrinter
{
private readonly OldPrinter _old;
public PrinterAdapter(OldPrinter old) => _old = old;
public void Print(string doc) => _old.PrintOldStyle(doc); // 変換して委譲
}
interface IRenderer { string Color(); }
class RedRenderer : IRenderer { public string Color() => "赤"; }
class Circle
{
private readonly IRenderer _renderer; // 実装への「橋」
public Circle(IRenderer renderer) => _renderer = renderer;
public void Draw() => Console.WriteLine($"{_renderer.Color()}で 円 を描画");
}
- Adapter: 既存クラスを望むインターフェースに「変換」して再利用する
- Bridge: 抽象(図形)と実装(描画方法)を分け、両方を独立に拡張できる
- どちらも「継承より委譲(has-a)」で柔軟性を得る
演習
ITemperature t = new CelsiusAdapter(new FahrenheitSensor());
Console.WriteLine(t.Celsius()); // 25
// TODO: 華氏で返す FahrenheitSensor を、摂氏で返す ITemperature に変換する
// CelsiusAdapter を定義してください(摂氏 = (華氏 - 32) * 5 / 9、77°F→25℃)
interface ITemperature { int Celsius(); }
class FahrenheitSensor { public int Fahrenheit() => 77; }
___
- 期待される出力:
25
ヒント1を見る
class CelsiusAdapter : ITemperature { private readonly FahrenheitSensor _s; public CelsiusAdapter(FahrenheitSensor s) => _s = s; public int Celsius() => (_s.Fahrenheit() - 32) * 5 / 9; }
ヒント2を見る
アダプタ内で華氏を摂氏に変換して返します
まとめ
- Adapterは既存クラスを望む形に変換する変換プラグ
- Bridgeは抽象と実装を分離し独立に拡張する
- 継承ではなく委譲で組み合わせる
次回: 入れ子と機能追加のCompositeとDecoratorです。