设计模式:适配器模式

适配器模式(Adapter Pattern),别名Wrapper。

适配器模式分为两种:对象适配器模式;类适配器模式。

理解类图需要知道的一些基本名词和符号:UML类图的几个名词及对应符号

对象适配器模式

Client想要调用某个具体Adaptee的方法,来实现某个功能A。最直接的做法是将Adaptee作为参数传入,然后让Client创建一个方法去调用。
如果Client也想调用另一个具体Adaptee的方法,也实现某个功能A。此时再将Adaptee作为参数传入,然后让Client创建一个方法去调用。
……
当Client想调用一百个类(甚至不止这些)的方法,来实现同一个功能A,那不就得添加一百个方法?真这么做,这个类就太臃肿了。并且,在某个具体的系统,极大部分的方法都不会被用到。

那么,提取出一个接口(Target),并让Client调用这个接口的方法不就行了吗?这样Client就不需要为某个具体的类添加方法。 问题就这样解决了,但还没结束。

既然有一个接口,那么不就意味着每个具体Adaptee都要实现这个接口了么?最理想的情况是,这些类刚好那个被调用的方法的方法名是相同的,直接加个implements xxx就行了(前提是你被允许修改这些类的代码)。

当然,现实并不是这么理想。它们的方法名往往不同,而你也不被允许修改它们的代码,更不用说去这些类里面去实现这些方法。

那TM怎么办?

先把已知条件整理出来:已知Client会调用Target接口的某个方法,但是具体类的方法名与接口的方法名不匹配,不能让具体类实现Target接口并传入Client。
问:如何让Client只调用Target接口的方法而间接地调用到具体类的方法?

答:创建一个类(MyAdapter),这个类里有一个方法,其方法名与Target定义的方法名完全一致(假设为sayHello())。接着将具体的类(假设为 RudeMan)传入这个新建的类,在 sayHello() 里面调用具体类的方法(假设为 sayFuck())。这就相当于给RudeMan的sayFuck()方法包装了另一个名字。
既然MyAdapter的方法名与Target定义的一致,那么就可以加上implements xxx了。

public class MyAdapter implements Target {
    private RudeMan rudeMan = null;
    
    public MyAdapter(RudeMan man) {
        rudeMan = man;
    }
    
    public void sayHello() {
        rudeMan.sayFuck();
    }
}

这时,MyAdapter类相当于是一个转接口,将具体类RudeMan的具体方法包装成另一个名字。并且MyAdapter实现了Target接口,可以传入Client供其调用。

类适配器模式

由于类适配器模式有时需要多重继承,而JAVA不支持,所以通常不使用类适配器模式。

对于类适配器模式的方式,你会看到这样的形式:

public class Adapter extends Adaptee implements Target {
……
}

这里extends是为了调用具体类Adaptee的方法,而implements是为了让Client调用已被Target定义好的方法。

从UML类图角度来对比两者

对象适配器模式:基本聚合
类适配器模式:泛化

posted @ 2017-01-12 15:55  schaepher  阅读(297)  评论(0编辑  收藏  举报