本文へスキップ
BecomeCoder

C#デザインパターンコース · 第1章 デザインパターン · レッスン5

構造①:Adapter / Bridge

ブラウザで完結

導入

構造に関するパターンは「クラスの組み合わせ方」を工夫します。Adapterは「互換性のない既存クラスを、望む形に変換する変換プラグ」、Bridgeは「機能の階層と実装の階層を分離」して独立に拡張できるようにします。

図解

flowchart LR
    C["利用側が期待する形<br/>ITarget"] --> AD["Adapter<br/>変換"]
    AD --> OLD["既存の別インターフェース<br/>(そのままでは使えない)"]
    style AD fill:#e1f5fe

サンプル

Adapterは「変換プラグ」、Bridgeは「橋渡し」。どちらも登場人物ごとにファイルを分けると関係がはっきりします(タブで切り替えられます)。

1. 利用側が期待する形(IPrinter.cs)。

public interface IPrinter
{
    void Print(string doc);
}

2. そのままでは使えない既存クラス(OldPrinter.cs)。メソッド名も引数も違う、書き換えられない資産だと思ってください。

public class OldPrinter
{
    public void PrintOldStyle(string s) => Console.WriteLine($"旧式印刷: {s}");
}

3. 変換プラグ(PrinterAdapter.cs)。IPrinterの顔をしながら、中ではOldPrinterに委譲します。

// Adapter: 既存の OldPrinter を、新しい IPrinter として使えるよう変換
public class PrinterAdapter : IPrinter
{
    private readonly OldPrinter _old;
    public PrinterAdapter(OldPrinter old) => _old = old;
    public void Print(string doc) => _old.PrintOldStyle(doc);   // 変換して委譲
}

4. 実装側の階層(IRenderer.cs / RedRenderer.cs)。Bridgeの「実装」の側です。色を増やすときはここにクラスを足します。

public interface IRenderer
{
    string Color();
}
public class RedRenderer : IRenderer
{
    public string Color() => "赤";
}

5. 機能側の階層(Circle.cs)。図形を増やすときはこちらにクラスを足します。色×図形の組み合わせ分のクラスを作らなくて済むのがBridgeの効き目です。

// Bridge: 「図形(抽象)」と「描画方法(実装)」を分離して独立に組み合わせる
public class Circle
{
    private readonly IRenderer _renderer;   // 実装への「橋」
    public Circle(IRenderer renderer) => _renderer = renderer;
    public void Draw() => Console.WriteLine($"{_renderer.Color()}で 円 を描画");
}

6. 組み立てて使う側(Program.cs)。

IPrinter printer = new PrinterAdapter(new OldPrinter());
printer.Print("資料");   // 旧式印刷: 資料

var redCircle = new Circle(new RedRenderer());
redCircle.Draw();        // 赤で 円 を描画
  • 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です。

実際に動かしてみよう

このレッスンのサンプルは、実務と同じように役割ごとの .cs ファイルへ分けてあります。下のエディタは最初からその複数ファイルが入った状態で、上のタブでファイルを切り替えられます。そのまま「▶ 実行」を押せば全ファイルをまとめてコンパイルして動かせるので、クラスを足したりファイルをまたいで書き換えたりしながら進めましょう(演習は本文のコードをコピーして Program.cs のタブに貼り付ければ試せます)。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。

次のレッスンへ →