浅析设计模式(七)——适配器模式
适配器模式(Adapter,结构型模式)
本文的结构
一、适配器模式的定义
定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
简单点理解:转换接口,进行适配。
从适配的方式来看,有2种可行的方式,即【类的适配器】和【对象的适配器】,换个说法就是【继承(接口实现)】和【组合】,两者的效果如下
继承(接口实现,is-a):
- 用一个具体的Adapter类对Adaptee和Target进行匹配,但无法匹配Adaptee的子类
- 作为Adaptee的子类,Adapter可重新定义Adaptee的部分行为
组合(has-a):
- 允许一个Adapter与多个Adaptee(其本身或者所有子类)一起工作
- 重新定义Adaptee的行为会比较困难,需要生成Adaptee的子类并且使得Adapter引用这个子类。
Java是单继承(类),多实现(接口)。在使用时,需要根据实际情况进行考虑。比如说,Adaptee和Target都是类,无法多重继承,就只能使用对象的适配器;如果需要使用到一群子类,使用对象的适配器进行组合(子类的父接口)会更加合适;如果仅仅是适配一个第三方接口,可以使用类的适配器进行适配,也可以使用对象的适配器进行适配。
举个不恰当的例子:spring工程中常见的service,也会调用到其他的service,可以看成是最简单粗暴的一种代理。。。
二、适配器模式的参与者及其角色
1. Target
- 定义Client使用的与特定领域相关的接口,即目标接口
2. Client
- 与符合Target接口的对象协同。
3. Adaptee
- 定义一个已经存在的接口,这个接口需要适配
4. Adapter
- 对Adaptee的接口与Target接口进行适配
三、适配器模式的类图
类的适配器模式:
对象的适配器模式:
四、适配器模式的示例
下面使用对象的适配器进行简单地说明。
这里有鸭子类和火鸡类,可以通过适配器将鸭子适配(伪装)成火鸡进行调用,反过来也可以将火鸡适配(伪装)成鸭子进行调用。
1. Target 和 3. Adaptee
鸭子类和火鸡类是需要相互进行适配的类。
鸭子及其实现类:
// 鸭子接口 public interface Duck { public void quack(); public void fly(); } // 鸭子实现类 public class MallardDuck implements Duck { @Override public void quack() { System.out.println("Quack"); } @Override public void fly() { System.out.println("I'm flying"); } }
火鸡及其实现类
1 // 火鸡接口 2 public interface Turkey { 3 public void gobble(); 4 public void fly(); 5 } 6 7 // 实现类 8 public class WildTurkey implements Turkey { 9 @Override 10 public void gobble() { 11 System.out.println("Gobble gobble"); 12 } 13 14 @Override 15 public void fly() { 16 System.out.println("I'm flying a short distance"); 17 } 18 }
2. Client
这里看成是正常的客户端调用,可以看到将火鸡Turkey进行适配包装后,可以和调用鸭子Duck一样调用适配后的火鸡。
鸭子Duck
1 public class DuckTestDrive { 2 public static void main(String[] args) { 3 Duck duck = new MallardDuck(); 4 5 Turkey turkey = new WildTurkey(); 6 Duck turkeyAdapter = new TurkeyAdapter(turkey); 7 8 System.out.println("The Turkey says..."); 9 turkey.gobble(); 10 turkey.fly(); 11 12 System.out.println("\nThe Duck says..."); 13 testDuck(duck); 14 15 System.out.println("\nThe TurkeyAdapter says..."); 16 testDuck(turkeyAdapter); 17 } 18 19 static void testDuck(Duck duck) { 20 duck.quack(); 21 duck.fly(); 22 } 23 }
火鸡Turkey
public class TurkeyTestDrive { public static void main(String[] args) { Duck duck = new MallardDuck(); Turkey duckAdapter = new DuckAdapter(duck); for(int i=0;i<10;i++) { System.out.println("The DuckAdapter says..."); duckAdapter.gobble(); duckAdapter.fly(); } } }
4. Adapter
火鸡适配成鸭子:
1 public class TurkeyAdapter implements Duck { 2 Turkey turkey; 3 4 public TurkeyAdapter(Turkey turkey) { 5 this.turkey = turkey; 6 } 7 8 public void setTurkey(Turkey turkey) { 9 this.turkey = turkey; 10 } 11 12 @Override 13 public void quack() { 14 turkey.gobble(); 15 } 16 17 @Override 18 public void fly() { 19 for (int i = 0; i < 5; i++) { 20 turkey.fly(); 21 } 22 } 23 }
鸭子适配成火鸡:
1 public class DuckAdapter implements Turkey { 2 Duck duck; 3 Random rand; 4 5 public DuckAdapter(Duck duck) { 6 this.duck = duck; 7 rand = new Random(); 8 } 9 10 public void setDuck(Duck duck) { 11 this.duck = duck; 12 } 13 14 @Override 15 public void gobble() { 16 duck.quack(); 17 } 18 19 @Override 20 public void fly() { 21 if (rand.nextInt(5) == 0) { 22 duck.fly(); 23 } 24 } 25 }
由此可以看到接口的适配和转换。
五、参考
1、参考《Head First设计模式》和GoF《设计模式:可复用面向对象软件的基础》