2
2

More than 5 years have passed since last update.

「HeadFirstデザインパターン」と「Rubyによるデザインパターン」を読んで Adapter パターン

Posted at

何番煎じか判りませんがお勉強メモを残します

HeadFirstデザインパターン」第7章
Rubyによるデザインパターン」第9章

Adapter パターン

オブジェクトアダプタとクラスアダプタ

  • アダプタパターンには実は2種類あるんです
  • アダプタパターンと言えばオブジェクトアダプタパターンを指しているものと考えて良い
  • クラスアダプタパターンを実装するには多重継承を使うので考えたくない(RubyもJavaも多重継承をサポートしていない)

「HeadFirstデザインパターン」でのJavaコードは(だいたい)こんな感じ

  • 鴨のように鳴き、鴨のように飛ぶものが居たら、それは鴨アダプタでラップされた七面鳥オブジェクトかもしれない
  • 鴨クラスを使用している旧システムのコードを変更せずに、七面鳥クラスを使用したい

鴨インタフェース => 鳴き、飛ぶ

public interface Duck {
  public void quack(); // 鳴く
  public void fly();
}

マガモクラス => ガーガー鳴き、飛ぶ

public class MallardDuck implements Duck {
  public void quack() {
    System.out.println("ガーガー");
  }

  public void fly() {
    System.out.println("飛ぶ");
  }
}

鴨クラスを使用している旧システムはこんな感じ これは変更できない

static void testDuck(Duck duck) {
  duck.quack();
  duck.fly();
}

Duck duck = new Duck();
testDuck(duck);

ここで上司が「今度、七面鳥オブジェクトも増えるんで対応ヨロ」と言い出したとしたら

七面鳥インタフェース => 鳴く

public interface Turkey {
  public void gobble(); // ゴロゴロ鳴くらしい(外国語)
  public void fly();
}

七面鳥クラス => ゴロゴロ鳴き、短い距離を飛ぶ

public class WildTurkey implements Turkey {
  public void gobble() {
    System.out.println("ゴロゴロ");
  }

  public void fly() {
    System.out.println("短い距離を飛ぶ");
  }
}

当然、旧システムには渡せない

WildTurkey turkey = new WildTurkey();
testDuck(turkey); // Duckインタフェースじゃないのでエラー

そこでアダプタです

public class TurkeyAdapter implements Duck {
  Turkey turkey;

  public TurkeyAdapter(Turkey turkey) {
    this.turkey = turkey;
  }

  public void quack() {
    turkey.gobble();
  }

  public void fly() {
    // 七面鳥は短い距離しか飛べないので5回くらい飛ばせてみる
    for(int i = 0; i < 5; i++) {
      turkey.fly();
    }
  }
}

旧システムに七面鳥を渡してみる

WildTurkey turkey = new WildTurkey();
Duck turkeyAdapter = new TurkeyAdapter(turkey)

testDuck(turkeyAdapter);
  • クラスのインタフェースをクライアントが期待する別のインタフェースに変換
  • 互換性がなく、そのままでは連携できないクラスを連携させる

「Rubyによるデザインパターン」でのRubyコードは(だいたい)こんな感じ

アダプタの説明は上記のJavaの例と(言いたい事は)同じ

だが代替手段が提示される。つまり

  • 七面鳥クラスをオープンして、メソッドを追加するだけでいいんじゃね?
  • アダプタ要らねんじゃね?
class WildTurkey
  def quack
    gobble
  end

  alias_method :original_fly, :fly

  def fly
    5.times { original_fly }
  end
end
  • 七面鳥クラスのインスタンス全ての振る舞いを変更するのはやりすぎじゃね?
  • そのインスタンスに特異メソッドを定義するだけでいいんじゃね?
turkey = WildTurkey.new

class << turkey
  def quack
    gobble
  end

  alias_method :original_fly, :fly

  def fly
    5.times { original_fly }
  end
end

RubyだとAdapterパターンを使うなって事?

  • インターフェースのサポートの為にクラスやインスタンスを変更するのは、Adapterパターンに比べてシンプルなコードになる
  • クラス内に潜り込んで変更するというこのテクニックは深刻なカプセル化違反を伴う
  • 変更するクラスの事を十分に熟知していない、インターフェースの不整合が広範囲に及んでいる、ような場合にはトラブルの原因となりえる
  • トレードオフ。Adapterパターンではいくらかの複雑化と引き換えにカプセル化を守ることができる
  • クラスの変更ではいくらかの単純化と引き換えに内部をいじるコストがかかる
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2