何番煎じか判りませんがお勉強メモを残します
「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パターンではいくらかの複雑化と引き換えにカプセル化を守ることができる
- クラスの変更ではいくらかの単純化と引き換えに内部をいじるコストがかかる