JAVA设计模式---适配器模式

来源:互联网 发布:c语言if语句例题 编辑:程序博客网 时间:2024/06/09 17:39

1.概念

在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。

分为:类适配器模式和对象适配器模式

2.类适配器模式

在这个模式中,Adapter与Adaptee是继承关系,这决定了这个适配器模式是类的


模式所涉及的角色:

①目标角色(Target):这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。

②源角色(Adaptee):现在需要适配的接口。

③适配器角色(Adapter):适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

⑵具体代码

总结一下:继承源角色,实现目标接口,补充接口存在而源角色不存在的方法。

目标角色(Target)

public interface Target {    /**     * 这是源类Adaptee也有的方法     */    public void sampleOperation1();     /**     * 这是源类Adapteee没有的方法     */    public void sampleOperation2(); }
上面给出的是目标角色的源代码,这个角色是以一个JAVA接口的形式实现的。可以看出,这个接口声明了两个方法:sampleOperation1()和sampleOperation2()。而源角色Adaptee是一个具体类,它有一个sampleOperation1()方法,但是没有sampleOperation2()方法。
源角色(Adaptee)

public class Adaptee {        public void sampleOperation1(){}}
适配器角色(Adapter)

适配器角色Adapter扩展了Adaptee,同时又实现了目标(Target)接口。由于Adaptee没有提供sampleOperation2()方法,而目标接口又要求这个方法,因此适配器角色Adapter实现了这个方法。

public class Adapter extends Adaptee implements Target {    /**     * 由于源类Adaptee没有方法sampleOperation2()     * 因此适配器补充上这个方法     */    @Override    public void sampleOperation2() {        //写相关的代码    }}
3.对象适配器模式
⑴与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。

建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。


从上图可以看出,Adaptee类并没有sampleOperation2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。

⑵具体代码

总结一下:适配器引入源角色,实现目标接口,源角色有的调用没有的补充。

目标角色(Target)

public interface Target {    /**     * 这是源类Adaptee也有的方法     */    public void sampleOperation1();     /**     * 这是源类Adapteee没有的方法     */    public void sampleOperation2(); }
源角色(Adaptee)

public class Adaptee {    public void sampleOperation1(){}    }
适配器角色(Adapter)

public class Adapter implements Target{    private Adaptee adaptee;        public Adapter(Adaptee adaptee){        this.adaptee = adaptee;    }    /**     * 源类Adaptee有方法sampleOperation1     * 因此适配器类直接委派即可     */    public void sampleOperation1(){        this.adaptee.sampleOperation1();    }    /**     * 源类Adaptee没有方法sampleOperation2     * 因此由适配器类需要补充此方法     */    public void sampleOperation2(){        //写相关的代码    }}
4.适配器模式的优缺点

⑴优点

①更好的复用性

系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

②更好的扩展性

在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

⑵缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

5.缺省适配器
缺省适配器模式是一种特殊的适配器模式,但这个适配器是由一个抽象类(使用抽象类可以避免这个类被实例化,非必须)实现的,并且在抽象类中要实现目标接口中所规定的所有方法,但很多方法的实现都是“平庸”的实现,也就是说,这些方法都是空方法。而具体的子类都要继承此抽象类。

鲁智深的故事:

和尚要做什么呢?吃斋、念经、打坐、撞钟、习武等。如果设计一个和尚接口,给出所有的和尚都需要实现的方法,那么这个接口应当如下:

public interface 和尚 {    public void 吃斋();    public void 念经();    public void 打坐();    public void 撞钟();    public void 习武();    public String getName();}
显然,所有的和尚类都应当实现接口所定义的全部方法,不然就根本通不过JAVA语言编辑器。像下面的鲁智深类就不行。

public class 鲁智深 implements 和尚{    public void 习武(){        拳打镇关西;        大闹五台山;        大闹桃花村;        火烧瓦官寺;        倒拔垂杨柳;    }    public String getName(){        return "鲁智深";    }}
它根本就通不过Java语言编译器。鲁智深类只有实现和尚接口的所有的方法才可以通过Java语言编译器,但这样鲁智深就不是鲁智深了。
我们做一个"天星"类来解决这个问题:

public abstract class 天星 implements 和尚 {    public void 吃斋(){}    public void 念经(){}    public void 打坐(){}    public void 撞钟(){}    public void 习武(){}    public String getName(){        return null;    }}
然后让鲁智深继承天星

public class 鲁智深 extends 天星{    public void 习武(){        拳打镇关西;        大闹五台山;        大闹桃花村;        火烧瓦官寺;        倒拔垂杨柳;    }    public String getName(){        return "鲁智深";    }}
这个抽象的天星类便是一个适配器类,鲁智深实际上借助于适配器模式达到了剃度的目的。

在很多情况下,必须让一个具体类实现某一个接口,但是这个类又用不到接口所规定的所有的方法。通常的处理方法是,这个具体类要实现所有的方法,那些有用的方法要有实现,那些没有用的方法也要有空的、平庸的实现。

这些空的方法是一种浪费,有时也是一种混乱。除非看过这些空方法的代码,程序员可能会以为这些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看过这些方法的源代码或是文档。

缺省适配模式可以很好的处理这一情况。可以设计一个抽象的适配器类实现接口,此抽象类要给接口所要求的每一种方法都提供一个空的方法。就像帮助了鲁智深的“上应天星”一样,此抽象类可以使它的具体子类免于被迫实现空的方法。

适配器模式的用意是要改变源的接口,以便于目标接口相容。缺省适配的用意稍有不同,它是为了方便建立一个不平庸的适配器类而提供的一种平庸实现。

在任何时候,如果不准备实现一个接口的所有方法时,就可以使用“缺省适配模式”制造一个抽象类,给出所有方法的平庸的具体实现。这样,从这个抽象类再继承下去的子类就不必实现所有的方法了。















原创粉丝点击