精华内容
下载资源
问答
  • 适配器模式详解

    2016-09-28 15:40:21
    适配器模式详解  作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。  各位好,我们本次...

    适配器模式详解

                      作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。

                      各位好,我们本次接着讨论第八个设计模式,适配器模式。

                      适配器模式从实现方式上分为两种,类适配器和对象适配器,这两种的区别在于实现方式上的不同,一种采用继承,一种采用组合的方式。

                      另外从使用目的上来说,也可以分为两种,特殊适配器和缺省适配器,这两种的区别在于使用目的上的不同,一种为了复用原有的代码并适配当前的接口,一种为了提供缺省的实现,避免子类需要实现不该实现的方法。

                      首先应该明白一点,适配器模式是补救措施,所以在系统设计过程中请忘掉这个设计模式,这个模式只是在你无可奈何时的补救方式。

                      那么我们什么时候使用这个模式呢?场景通常情况下是,系统中有一套完整的类结构,而我们需要利用其中某一个类的功能(通俗点说可以说是方法),但是我们的客户端只认识另外一个和这个类结构不相关的接口,这时候就是适配器模式发挥的时候了,我们可以将这个现有的类与我们的目标接口进行适配,最终获得一个符合需要的接口并且包含待复用的类的功能的类。

                      接下来我们举一个例子,比如我们在观察者一章中就提到一个问题,就是说观察者模式的一个缺点,即如果一个现有的类没有实现Observer接口,那么我们就无法将这个类作为观察者加入到被观察者的观察者列表中了,这实在太遗憾了。

                      在这个问题中,我们需要得到一个Observer接口的类,但是又想用原有的类的功能,但是我们又改不了这个原来的类的代码,或者原来的类有一个完整的类体系,我们不希望破坏它,那么适配器模式就是你的不二之选了。

                      我们举个具体的例子,比如我们希望将HashMap这个类加到观察者列表里,在被观察者产生变化时,假设我们要清空整个MAP。但是现在加不进去啊,为什么呢?

                      因为Observable的观察者列表只认识Observer这个接口,它不认识HashMap,怎么办呢?

                      这种情况下,我们就可以使用类适配器的方式将我们的HashMap做点手脚,刚才已经说了,类适配器采用继承的方式,那么我们写出如下适配器。

    public class HashMapObserverAdapter<K, V> extends HashMap<K, V> implements Observer{
    
        public void update(Observable o, Object arg) {
            //被观察者变化时,清空Map
            super.clear();
        }
    
    }

                     即我们继承我们希望复用其功能的类,并且实现我们想适配的接口,在这里就是Observer,那么就会产生一个适配器,这个适配器具有原有类(即HashMap)的功能,又具有观察者接口,所以这个适配器现在可以加入到观察者列表了。

                     看,类适配器很简单吧?那么下面我们来看看对象适配器,刚才说了对象适配器是采用组合的方式实现。

                     为什么要采用组合呢?上面的方式不是很好吗?

                     究其根本,是因为JAVA单继承的原因,一个JAVA类只能有一个父类,所以当我们要适配的对象是两个类的时候,你怎么办呢?你难道要将两个类全部写到extends后面吗,如果你这么做了,那么编译器会表示它的不满的。

                     我们还是拿观察者模式那一章的例子来说(观察者模式比较惨,老要适配器模式擦屁股),比如我们现在有一个写好的类,假设就是个实体类吧。如下。

    public class User extends BaseEntity{
        private Integer id;
        private String name;
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

                     看到了吧,我们的实体类大部分都是继承自BaseEntity的,那现在你怎么办吧,你要想具有被观察者的功能还要继承Observable类,你说你怎么继承吧。

                     你是不是想说,那我的User不继承BaseEntity不就完事了,我把BaseEntity里面的东西全部挪动到User类,或者我不继承Observable了,把Observable里面的东西全部挪到User类里面。

                     这并不是不行,但是这是个很大的隐患,比如我们项目到时候要针对BaseEntity的子类进行扫描,用来做一些事情,这时候如果User没继承BaseEntity,那么你就会遗漏掉这个类,这会破坏你的继承体系,付出太大了。

                     相反,如果你不继承Observable,那么你的User类看起来会非常杂乱,而且假设我现在不仅User类可以被观察了,我的Person类,Employee都能被观察了,你难道要把Observable的代码COPY三次到这三个类里面吗?

                    不要忘了刚才说的,适配器模式就是为了帮助我们复用代码的,这里使用适配器模式就可以帮我们复用Observable的代码或者说功能。

                    基于上面LZ的讨论,我们做出如下适配器,这里采用的对象适配器。

    //我们继承User,组合Observable.
    public class ObservableUser extends User{
        
        private Observable observable = new Observable();
    
        public synchronized void addObserver(Observer o) {
            observable.addObserver(o);
        }
    
        public synchronized void deleteObserver(Observer o) {
            observable.deleteObserver(o);
        }
    
        public void notifyObservers() {
            observable.notifyObservers();
        }
    
        public void notifyObservers(Object arg) {
            observable.notifyObservers(arg);
        }
    
        public synchronized void deleteObservers() {
            observable.deleteObservers();
        }
    
        protected synchronized void setChanged() {
            observable.setChanged();
        }
    
        protected synchronized void clearChanged() {
            observable.clearChanged();
        }
    
        public synchronized boolean hasChanged() {
            return observable.hasChanged();
        }
    
        public synchronized int countObservers() {
            return observable.countObservers();
        }
        
        
    }

                  我们继承User,而不是继承Observable,这个原因刚才已经说过了,我们不能破坏项目中的继承体系,所以现在可观察的User(ObservableUser)依然处于我们实体的继承体系中,另外如果想让ObservableUser具有User的属性,则需要将User的属性改为protected。

                  这下好了,我们有了可观察的User了。不过LZ早就说过,设计模式要活用,这里明显不是最好的解决方案。因为我们要是还有Person,Employee类都要具有可观察的功能的话,那其实也相当惨,因为下面那些Observable的方法我们还要再复制一遍。

                  提示到这里,不知各位想到更好的解决方案了吗?尤其是新手可以好好思考下。

                  LZ这里给出最终相对来说比较好的解决方案,那就是我们定义如下可观察的基类。

    //我们扩展BaseEntity,适配出来一个可观察的实体基类
    public class BaseObservableEntity extends BaseEntity{
    
        private Observable observable = new Observable();
    
        public synchronized void addObserver(Observer o) {
            observable.addObserver(o);
        }
    
        public synchronized void deleteObserver(Observer o) {
            observable.deleteObserver(o);
        }
    
        public void notifyObservers() {
            observable.notifyObservers();
        }
    
        public void notifyObservers(Object arg) {
            observable.notifyObservers(arg);
        }
    
        public synchronized void deleteObservers() {
            observable.deleteObservers();
        }
    
        protected synchronized void setChanged() {
            observable.setChanged();
        }
    
        protected synchronized void clearChanged() {
            observable.clearChanged();
        }
    
        public synchronized boolean hasChanged() {
            return observable.hasChanged();
        }
    
        public synchronized int countObservers() {
            return observable.countObservers();
        }
        
    }

                  这下好了,现在我们的User,Person,Employee要是想具有可被观察的功能,那就改去继承我们适配好的BaseObservableEntity就好了,而且由于BaseObservableEntity继承了BaseEntity,所以他们三个依然处于我们实体的继承体系中,而且由于我们的BaseObservableEntity是新增的扩展基类,所以不会对原来的继承体系造成破坏。

                  适配器模式的用法还是比较清晰的,我们以上两种方式都是为了复用现有的代码而采用的适配器模式,LZ刚才说了,根据目的的不同,适配器模式也可以分为两种,那么上述便是第一种,可称为定制适配器,还有另外一种称为缺省适配器

                  首先我们得先说下缺省适配器为什么要出现,因为适配器模式大部分情况下是为了补救,所以既然补救,那么肯定是历史原因造成的我们需要使用这个模式。

                  我们来看看缺省适配器的历史来由,不知各位还是否记得在第一章总纲中,LZ曾经提到过一个原则,最小接口原则。

                  这个原则所表达的思想是说接口的行为应该尽量的少,那么还记得LZ当时说如果你没做到的话会产生什么情况吗?

                  结果就是实现这个接口的子类,很可能出现很多方法是空着的情况,因为你的接口设计的过大,导致接口中原本不该出现的方法出现了,结果现在子类根本用不上这个方法,但由于JAVA语言规则的原因,实现一个接口必须实现它的全部方法,所以我们的子类不得不被迫写一堆空方法在那,只为了编译通过。

                  所以为了解决这一问题,缺省适配器就出现了。比如我们有如下接口。

    public interface Person {
        
        void speak();
        
        void listen();
        
        void work();
        
    }

                     这是一个人的接口,这个接口表示了人可以说话,听和工作,假设是两年前的LZ,还在家待业呢,LZ没工作啊,但是LZ也是个人啊,所以LZ要实现这个接口,所以LZ只能把work方法抄下来空着放在那了,假设LZ是个聋哑人,好吧,三个方法都要空着了,但是LZ表示,LZ是人,LZ一定要实现Person接口。

                     当然,上述只是举个例子,但是真实项目当中也会出现类似的情况,那么怎么办呢?

                     这下来了,我们的缺省适配器来了,如下。

    public class DefaultPerson implements Person{
    
        public void speak() {
        }
    
        public void listen() {
        }
    
        public void work() {
        }
    
    }

                     我们创造一个Person接口的默认实现,它里面都是一些默认的方法,当然这里因为没什么可写的就空着了,实际当中可能会加入一些默认情况下的操作,比如如果方法返回结果整数,那么我们在缺省适配器中可以默认返回个0。

                     这下好了,LZ只要继承这个默认的适配器(DefaultPerson),然后覆盖掉LZ感兴趣的方法就行了,比如speak和listen,至于work,由于适配器帮我们提供了默认的实现,所以就不需要再写了。

                     这种情况其实蛮多的,因为接口设计的最小化只是理想状态,难免会有一些实现类,对其中某些方法不感兴趣,这时候,如果方法过多,子类也很多,并且子类的大部分方法都是空着的,那么就可以采取这种方式了。

                     当然,这样做违背了里氏替换原则,但是上面的做法原本就违背了接口的最小化原则,所以我们在真正使用时要权衡二者的利弊,到底我们需要的是什么。所以从此也可以看出来,原则只是指导,并不一定也不可能全部满足,所以我们一定要学会取舍。

                     总结下两种实现方式的适配器所使用的场景,两者都是为了将已有类的代码复用并且适配到客户端需要的接口上去。

                     1,第一种类适配器,一般是针对适配目标是接口的情况下使用。

                     2,第二种对象适配器,一般是针对适配目标是类或者是需要复用的对象多于一个的时候使用,这里再专门提示一下,对象适配器有时候是为了将多个类一起适配,所以才不得不使用组合的方式,而且我们采用对象适配器的时候,继承也不是必须的,而是根据实际的类之间的关系来进行处理,上述例子当中一定要直接或间接的继承自BaseEntity是为了不破坏我们原来的继承体系,但有些情况下这并不是必须的。

                     对于第三个缺省适配器,一般是为了弥补接口过大所犯下的过错,但是也请注意衡量利弊,权衡好以后再考虑是否要使用缺省适配器。

                     好了,本次适配器模式的分享就到此结束了,希望各位可以从中得到点收获。

                     最后,感谢您的收看。

                     下期预告,模板方法模式。






    版权声明:本文版权归作者(左潇龙)所有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    转自:http://www.zuoxiaolong.com/blog/article.ftl?id=117

    展开全文
  • 适配器模式 详解

    2016-11-07 10:00:00
    定义 将一个类转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作;...适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口; 适用场景 已经存在的类的...

    定义

    将一个类转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作;

    创建型模式


    角色

    • 目标接口(Flyweight):客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口;
    • 需要适配的类(Adaptee):需要适配的类或适配者类;
    • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口;

    适用场景

    • 已经存在的类的接口不符合我们的需求;
    • 创建一个可以复用的类,使得该类可以与其它不相关的类或者不可预见的类(即那些接口可能不一定兼容的类)协同工作;
    • 在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类;

    实现代码

    /**
     * Created by George on 16/7/2.
     */
    
    // 鸭子
    var Duck = function () {};
    
    Duck.prototype.fly = function () {
        throw new Error("该方法必须被重写");
    };
    
    Duck.prototype.quack = function () {
        throw new Error("该方法必须被重写");
    };
    
    // 火鸡
    var Turkey = function () {};
    
    Turkey.prototype.fly = function () {
        throw new Error("该方法必须被重写");
    };
    
    Turkey.prototype.gobble = function () {
        throw new Error("该方法必须被重写");
    };
    
    //具体的鸭子
    var MallardDuck = function () {
        Duck.apply(this);
    };
    //原型
    MallardDuck.prototype = new Duck();
    
    MallardDuck.prototype.fly = function () {
        console.log("鸭子飞翔");
    };
    
    MallardDuck.prototype.quack = function () {
        console.log("鸭子嘎嘎");
    };
    
    // 具体火鸡
    var WildTurkey = function () {
        Turkey.apply(this);
    };
    //原型
    WildTurkey.prototype = new Turkey();
    
    WildTurkey.prototype.fly = function () {
        console.log("火鸡飞翔");
    };
    
    WildTurkey.prototype.gobble = function () {
        console.log("火鸡咯咯");
    };
    
    //为了让火鸡也能支持鸭子的quack方法
    var TurkeyAdapter = function (oTurkey) {
        Duck.apply(this);
        this.oTurkey = oTurkey;
    };
    
    TurkeyAdapter.prototype = new Duck();
    
    TurkeyAdapter.prototype.quack = function () {
        this.oTurkey.gobble();
    };
    
    TurkeyAdapter.prototype.fly = function () {
        var nFly = 0;
        var nLenFly = 5;
        for (; nFly < nLenFly;) {
            this.oTurkey.fly();
            nFly = nFly + 1;
        }
    };
    
    var oMallardDuck = new MallardDuck();
    var oWildTurkey = new WildTurkey();
    var oTurkeyAdapter = new TurkeyAdapter(oWildTurkey);
    
    //原有的鸭子行为;
    oMallardDuck.fly();
    oMallardDuck.quack(); //鸭子叫
    console.log("------------");
    //原有的火鸡行为
    oWildTurkey.fly();
    oWildTurkey.gobble();  //火鸡叫
    console.log("------------");
    //适配器火鸡的行为
    oTurkeyAdapter.fly();
    oTurkeyAdapter.quack();  
    

    优缺点

    • 优点

      通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的;
      复用了现存的类,解决了现存类和复用环境要求不一致的问题;
      将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有的代码;
      一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口;

    • 缺点

      对于对象适配器来说,更换适配器的实现过程比较复杂;

    转载于:https://www.cnblogs.com/George1994/p/6037669.html

    展开全文
  • 主要介绍了Android Spinner与适配器模式详解相关资料,并附代码实例,需要的朋友可以参考下
  • 主要介绍了深入理解JavaScript系列(39):设计模式之适配器模式详解,适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),需要的朋友可以参考下
  • Java设计模式-3种适配器模式详解 文章目录Java设计模式-3种适配器模式详解0.前言1.类适配器1.1类适配器简介1.2 类适配器模式中的角色1.3 类适配器模式UML类图1.4 代码实现1.5 测试编码与分析1.6 阶段小结2.对象...

    Java设计模式-3种适配器模式详解


    此文为课后笔记,视频资源:尚硅谷图解Java设计模式韩顺平老师2019力作
    3种适配器模式:类适配器模式,对象适配器模式,接口适配器模式

    0.前言

    讲到适配器,我最先想到的就是电源适配器(俗称充电头),以手机为例,手机不能直接接受220V的交流电,需要通过电源适配器处理一下,转换成手机能接受的电压(比如直流电压5V)。适配器模式中的适配器也是干这种事的,解决两个类不兼容的问题,加一些中间处理操作,使得他们兼容。

    适配器模式(AdapterPattern):将一个类的接口(src原类/原接口)转换成客户希望的另外一个接口(dst目标类),使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

    适配器模式又分为类适配器模式,对象适配器模式,接口适配器模式三种。他们的区别只是src原类在适配器类种出现的方式不同

    下面的代码将模拟这样的场景:220V交流电——>适配器——>5V直流电

    具体到代码即:源电压类,适配器类,5V直流电接口,手机类,测试类

    其中源电压类,适配器类都是一样的先贴出来:

    源电压类(被适配的类):

    /**
     * 适配器模式——源类
     * 被适配的类
     * 220V交流电
     */
    public class VoltageSrc {
        private int voltageSize=220;
        private String voltageType="交流";
        //输出源电压 220v
        public int outputVoltageSize(){
            System.out.println("源电压 输出大小为 "+voltageSize+" V");
            return voltageSize;
        }
        //输出源电压 220v
        public String outputVoltageType(){
            System.out.println("源电压 输出类型为"+voltageType);
            return voltageType;
        }
    }
    

    适配后的结果(目标接口类)

    /**
     * 目标接口
     * 将来手机要使用的
     */
    public interface IVoltageDst {
        //输出目标电压 大小 5v
         int outputVoltageDstSize();
    
        //输出目标电压 类型 直流
         String outputVoltageDstType();
    }
    

    1.类适配器

    1.1类适配器简介

    在类适配器中,Src源类(被适配的类)以类的形式出现在适配器中,即源类是适配器的父类。

    所以适配器类需要继承src源类,实现dst目标接口:实现目标接口的方法,在方法中通过调用父类方法以及转换操作完成适配。

    1.2 类适配器模式中的角色

    • Target目标接口:目标接口类,定义最终需要的方法,在该例中即IVoltageDst接口
    • Adaptee被适配者:被适配的类(src源类),持有既定的变量与在方法,该例子中即VoltageSrct类
    • Adapter适配器:适配器类,将被适配者中的既定变量与方法转换成目标接口适合的,即本例中ClassAdapter类。
    • Client请求者/使用者:请求类,即本例中的Phone手机类

    1.3 类适配器模式UML类图

    类适配器模式UML类图

    1.4 代码实现

    该例子中适配器类代码如下:

    
    /**
     * 适配器模式
     * 1.类适配器  继承src源类 实现dst目标接口
     */
    public class ClassAdapter extends VoltageSrc implements IVoltageDst {
        @Override
        public int outputVoltageDstSize() {
            //首先获取源电压 大小 类型
            int voltageSize = outputVoltageSize();//使用父类的方法 获取源电压大小
            //模拟源电压到目标电压的转换过程 电压大小转换
            return voltageSize / 44;//硬核转换,意思一下
        }
    
        @Override
        public String outputVoltageDstType() {
            String voltageType = outputVoltageType();//使用父类的方法 获取源电压类型
            //模拟源电压到目标电压的转换过程 电压类型转换
            if (voltageType.equals("交流")){
                voltageType = "直流";//硬核转换,意思一下
            }
            return voltageType;
        }
    }
    

    手机类:

    编写充电方法,传入的参数为目标接口的实例,完成充电操作(先判断电压大小和类型)

    public class Phone {
        //充电
        public void charging(IVoltageDst iVoltageDst){
            if (iVoltageDst.outputVoltageDstSize()==5
                    && iVoltageDst.outputVoltageDstType().equals("直流")){
                System.out.println("电压适配成功,手机冲电ing...");
            }else {
                System.out.println("电压适配失败,手机充不了电2333");
            }
        }
    }
    

    1.5 测试编码与分析

    创建一个手机类的对象,调用充电方法,传入的参数为适配器类的对象

    public class Test {
        public static void main(String[] args) {
                //类适配器测试
                System.out.println("适配器模式 1.类适配器测试");
                Phone phone=new Phone();
                phone.charging(new ClassAdapter());//传入适配器实例
        }
    }
    

    测试结果

    适配器模式 1.类适配器测试
    源电压 输出大小为 220 V
    源电压 输出类型为直流
    电压适配成功,手机冲电ing...
    

    结果分析:

    • 因为适配器完成了目标接口的实例化(implements IVoltageDst),所以适配器实例可以作为参数传入手机类Phone的充电方法charging()。

    • 因为适配器完成了源电压到目标电压的转换(220V交流电—>5V直流电),所以充电可以正常进行。

    1.6 阶段小结

    缺点:

    • 由于需要继承src源类(被适配的类),加上Java单继承机制,导致适配目标只能为接口。
    • src源类(被适配的类)中的方法都会在适配器类中暴露

    优点:

    • 由于继承自src源类(被适配的类),所以可重写该类中的方法,增加适配器的灵活性

    2.对象适配器

    2.1 简介

    对象适配器基本于类适配器一致,区别是:

    在类适配器中,Src源类(被适配的类)以类的形式出现在适配器中,即源类是适配器的父类。

    在对象适配器中,持有src源类(被适配的类),即src源类作为适配器的属性出现。

    所以适配器类只需要实现dst目标接口:实现目标接口的方法,在方法中通过调用持有src源类实例的方法以及转换操作完成适配。

    2.2 对象适配器UML类图

    对象适配器中的角色与类适配器中的一致,不重复写了。

    UML类图略有改变,如下所示:
    对象适配器UML类图

    2.3 代码实现

    需要修改适配器类:

    /**
     * 适配器模式
     * 2.对象适配器
     *
     */
    public class ObjectAdapter implements IVoltageDst {
        VoltageSrc voltageSrc;//持有被适配类的对象 聚合关系
    
        //构造方法,传入被适配类的对象
        public ObjectAdapter(VoltageSrc voltageSrc) {
            this.voltageSrc = voltageSrc;
        }
    
        @Override
        public int outputVoltageDstSize() {
            return 0;
        }
    
        @Override
        public String outputVoltageDstType() {
            return null;
        }
    }
    

    2.4 测试编码与分析

    测试类也需要做些微调,Test类修改如下:

    public class Test {
        public static void main(String[] args) {
            //类适配器测试
            System.out.println("适配器模式 1.类适配器测试");
            Phone phone = new Phone();
            phone.charging(new ObjectAdapter(new VoltageSrc()));//传入适配器,其构造函数传入被适配类的实例
        }
    }
    

    测试结果

    适配器模式 2.对象适配器测试
    源电压 输出大小为 220 V
    适配电压大小完成,适配完后电压大小:5 V 
    源电压 输出类型为交流
    适配电压类型完成,适配完后电压类型:直流
    电压适配成功,手机冲电ing...
    

    2.5 阶段小结

    对象适配器与类适配器思想一致,只是实现方式由继承src源类(被适配的类)改为持有,利用到了合成复用原则(组合代替继承),所以解决了类适配器模式中适配器类必须继承Src源类的局限,而且不再要求dst(最终适配目标)必须是接口。

    3.接口适配器

    3.1 接口适配器简介

    接口适配器模式也称缺省适配器模式,适用于一个类想使用一个接口中的部分方法,适配后只需实现需使用的方法即可。

    3.2 接口适配器实现原理

    根据上述简介的描述,如何实现的适配呢?其实就是设计一个抽象类实现原接口,每个方法都默认实现(即空实现),该抽象类就是接口适配器类。之后若只需使用原接口中的部分方法,则继承接口适配器类,便可选择性的实现所需要的方法。

    下面以一个稍微具体一点的例子展现接口适配器:

    原接口(被适配的接口)有4个方法:methodA(),methodB(),methodC(),methodD()

    接口请求类/使用类1(ClientA)只需要使用原接口中的methodA(),methodC()

    接口请求类/使用类2(ClientB)只需要使用原接口中的methodB(),methodC()

    如果不使用接口适配器的话,代码如下:

    原接口(被适配的接口):

    /**
     * 原接口 被适配的接口类
     */
    public interface ISrc {
        void methodA();
        void methodB();
        void methodC();
        void methodD();
    }
    

    接口请求类/使用类1(ClientA):

    /**
     * 接口请求类/使用类 A
     *
     * 只需要使用原接口中的methodA(),methodC()
     */
    public class ClientA implements ISrc {
        @Override
        public void methodA() {
            //需要使用,重写后进行需要的操作
        }
        @Override
        public void methodB() {
            //需要使用,重写后进行需要的操作
        }
        @Override
        public void methodC() {
            //不需要使用,但依然要重写,空实现
        }
        @Override
        public void methodD() {
            //不需要使用,但依然要重写,空实现
        }
    }
    

    同理,接口请求类/使用类2(ClientB):

    /**
     * 接口请求类/使用类 B
     * <p>
     * 只需要使用原接口中的methodB(),methodC()
     */
    public class ClientB implements ISrc {
        @Override
        public void methodA() {
            //不需要使用,但依然要重写,空实现
        }
        @Override
        public void methodB() {
            //需要使用,重写后进行需要的操作
        }
        @Override
        public void methodC() {
            //需要使用,重写后进行需要的操作
        }
        @Override
        public void methodD() {
            //不需要使用,但依然要重写,空实现
        }
    }
    

    可看到,那些请求类不需要使用的方法,必须要重写(空实现)。随着后续请求类越多,这要重复的代码就会越来越多。而接口适配器就是消除请求类不要使用的方法的空实现,将空实现统一在一起(建立实现接口的抽象类:接口适配器类)。这样请求类就只需实现自己需要的方法即可。

    3.3 代码实现

    改造上述的例子,创建接口适配器类,即一个实现原接口的抽象类,每个方法都空实现:

    /**
     * 适配器模式
     * 3.接口适配器
     * 创建接口适配器类,即一个实现原接口的抽象类,每个方法都空实现
     */
    public abstract class InterfaceAdapter implements ISrc {
        @Override
        public void methodA() {
    
        }
    
        @Override
        public void methodB() {
            
        }
        
        @Override
        public void methodC() {
            
        }
        
        @Override
        public void methodD() {
            
        }
    }
    

    这样之后,请求类只需要继承自该适配器类,之后实现所需要使用的方法即可

    修改后的 接口请求类/使用类1(ClientA):

    /**
     * 接口请求类/使用类 A
     *
     * 只需要使用原接口中的methodA(),methodC()
     */
    public class ClientA extends InterfaceAdapter {
        @Override
        public void methodA() {
            //需要使用,重写后进行需要的操作
        }
    
        @Override
        public void methodB() {
            //需要使用,重写后进行需要的操作
        }
    }
    

    同理修改后的 接口请求类/使用类2(ClientB):

    public class ClientB extends InterfaceAdapter {
        @Override
        public void methodB() {
            //需要使用,重写后进行需要的操作
        }
    
        @Override
        public void methodC() {
            //需要使用,重写后进行需要的操作
        }
    }
    

    3.4 阶段小结

    优点:减少了大量不必要的代码,之前有大量不需使用的方法,都需空实现。使用了接口适配器抽象类后,该抽象类的子类可选择性的覆盖父类方法来实现需求。

    展开全文
  • 主要为大家详细介绍了JavaScript适配器模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Adapter Pattern适配器模式细说适配器模式细说适配器模式定义UML图示场景场景一适配器分类类的适配器对象适配器接口适配器 细说适配器模式 提示: 博主:章飞 _906285288的博客 博客地址:...

    细说适配器模式

    提示:
    博主:章飞 _906285288的博客
    博客地址:http://blog.csdn.net/qq_29924041


    细说适配器模式

    适配器是作为两个不兼容设备之间的中间产物,在生活中这种适配器设备可以说随处可见,作用无非就是把1接口类型,转成2接口类型,如现在的TypeC接口转USB接口,OTG接口,大型设备类比如说变压器,可以将330V工业用电,转换成220V的家用电,新能源汽车的充电桩,等等的一切,一句话,生活离不开适配器。

    定义

    适配器模式(Adapter Pattern)
    Convert the interface of a class into annther interface clients except .Adapter lets classes work together that couldn’t otherwise because of incompatible interface;(将一个类的接口转换成客户所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作)

    适配器模式又叫做变压器模式,也叫做包装模式,但是包装模式并不止一种,还包括了装饰者模式

    UML图示

    在这里插入图片描述
    引用一张来自百度百科中的图,从中可以看到具体有一些这样的角色:
    1:Target :客户所期待的接口,也就是客户使用的接口类型,类似USB接口
    2:Adapter 适配器接口,通过在内部包装一个Adaptee对象 转接的设备
    3:Adaptee 具体需要转换适配的对象,类似typec接口

    一个typec转usb的转接头中,typec是需要转换的头,转换器为适配器,USB口则是另一个输出口

    场景

    场景一

    就拿我自己的mac做个例子吧,我的mac是17款的,拿到手的时候有点懵逼,苹果公司把以前的hdmi,usb口都给取消了,就留了两个typeC给我,没办法,自己要淘宝淘一个TypeC转接口啊,所以花了几十块钱买了个转接口,一个TypeC可以转TypeC,可以转USB口,可以转VGA口,可以转HDMI口。。在这个案例中,我们的目标对象是多个,但是可以归于一个转换器对象中,也就是Target,而TypeC则是Adaptee,硬件设备则是哪个Adapter。

    适配器分类

    适配器模式在具体使用的时候,具有以下几种分类,每种分类其实对应的场景其实会有点区别

    类的适配器

    类的适配器是基于类的角度来说的,
    Adapter 类继承Adaptee (被适配类),同时实现Target 接口(因为 Java 不支持多继承,所以只能通过接口的方法来实现多继承),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。采用的是继承的形式实现适配器模式

    代码一

    定义一个具体的转换器

    package src.com.zzf.designpattern.adapterpattern.demo4;
    
    public interface IPortExchangeTarget {
    	void useAsUsb();
    	void useAsHdmi();
    	void useAsAndroid();
    	void useAsVga();
    }
    
    

    苹果转换器

    package src.com.zzf.designpattern.adapterpattern.demo4;
    
    public class ApplePortExchanger implements IPortExchangeTarget {
    
    	@Override
    	public void useAsUsb() {
    		// TODO Auto-generated method stub
    		System.out.println("使用USB口");
    	}
    
    	@Override
    	public void useAsHdmi() {
    		// TODO Auto-generated method stub
    		System.out.println("使用hdmi口");
    	}
    
    	@Override
    	public void useAsAndroid() {
    		// TODO Auto-generated method stub
    		System.out.println("使用Android口");
    	}
    
    	@Override
    	public void useAsVga() {
    		// TODO Auto-generated method stub
    		System.out.println("使用Vga口");
    	}
    
    }
    

    TypeC口要转换的对象

    package src.com.zzf.designpattern.adapterpattern.demo4;
    
    public class TypeCEqupAdaptee {
    	protected String nameString = "";
    	public  TypeCEqupAdaptee() {
    		nameString = "TypeC接口";
    	}
    }
    
    

    将TypeC设备转换成多口设备

    package src.com.zzf.designpattern.adapterpattern.demo4;
    
    public class TypeCPortAdapter extends TypeCEqupAdaptee implements IPortExchangeTarget{
    
    	@Override
    	public void useAsUsb() {
    		// TODO Auto-generated method stub
    		System.out.println(super.nameString+"-----转换成--->USB");
    		System.out.println("使用USB口");
    	}
    
    	@Override
    	public void useAsHdmi() {
    		// TODO Auto-generated method stub
    		System.out.println(super.nameString+"-----转换成--->hdmi");
    		System.out.println("使用hdmi口");
    	}
    
    	@Override
    	public void useAsAndroid() {
    		// TODO Auto-generated method stub
    		System.out.println(super.nameString+"-----转换成--->Android");
    		System.out.println("使用Android口");
    	}
    
    	@Override
    	public void useAsVga() {
    		// TODO Auto-generated method stub
    		System.out.println(super.nameString+"-----转换成--->vga");
    		System.out.println("使用vga口");
    	}
    
    }
    
    
    package src.com.zzf.designpattern.adapterpattern.demo4;
    
    public class Test {
    	public static void main(String[] args) {
    		IPortExchangeTarget  portExchangeTarget = new TypeCPortAdapter();
    		portExchangeTarget.useAsAndroid();
    		portExchangeTarget.useAsHdmi();
    		portExchangeTarget.useAsUsb();
    		portExchangeTarget.useAsVga();
    	}
    }
    
    

    测试代码如上所示。
    可以看到,在类的适配器中,使用的是继承,和修改的形式,然后通过对被继承的Adaptee对象的方法进行内部调用,修改,然后转换成我们要使用的方法,不会具体去操作对象

    对象适配器

    对象适配器其实就是拿到具体的对象,通过对具体对象的操作,然后做转换。
    它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式

    举个例子,我现在需要一个适配器,既可以将typec口转换成需要的接口,同时也可以将usb口转换成需要的接口类型。那么这个时候,可以使用类适配器,但是对象适配器可能相对类说更合适一点。

    代码二

    目标接口

    public interface IPortExchangeTarget {
    	void useAsUsb();
    	void useAsHdmi();
    	void useAsAndroid();
    	void useAsVga();
    }
    

    抽象的接口设备,可以是TypeC,也可以是USB

    package src.com.zzf.designpattern.adapterpattern.demo5;
    
    public abstract class InterFace {
    	protected String nameString = "";
    	public  InterFace(String name) {
    		nameString = name;
    	}
    }
    
    

    具体的对象适配器,通过传入不同的接口,进行转换

    package src.com.zzf.designpattern.adapterpattern.demo5;
    
    public class UsbEqupAdaptee extends InterFace{
    
    	public UsbEqupAdaptee() {
    		super("USB接口");
    		// TODO Auto-generated constructor stub
    	}
    
    }
    
    
    package src.com.zzf.designpattern.adapterpattern.demo5;
    
    public class TypeCEqupAdaptee extends InterFace{
    
    	public TypeCEqupAdaptee() {
    		super("TypeC接口");
    		// TODO Auto-generated constructor stub
    	}
    
    }
    
    
    package src.com.zzf.designpattern.adapterpattern.demo5;
    
    import src.com.zzf.designpattern.adapterpattern.demo4.IPortExchangeTarget;
    
    public class ExchangeAdapter implements IPortExchangeTarget{
    	
    	public InterFace interFace = null;
    	
    	public  ExchangeAdapter(InterFace interFace) {
    		this.interFace = interFace;
    	}
    	
    	@Override
    	public void useAsUsb() {
    		// TODO Auto-generated method stub
    		System.out.println(interFace.nameString+"转换成USB");
    		System.out.println("使用USB口");
    	}
    
    	@Override
    	public void useAsHdmi() {
    		// TODO Auto-generated method stub
    		System.out.println(interFace.nameString+"转换成Hdmi");
    		System.out.println("使用Hdmi口");
    	}
    
    	@Override
    	public void useAsAndroid() {
    		// TODO Auto-generated method stub
    		System.out.println(interFace.nameString+"转换成Android");
    		System.out.println("使用Android口");
    	}
    
    	@Override
    	public void useAsVga() {
    		// TODO Auto-generated method stub
    		System.out.println(interFace.nameString+"转换成Vga");
    		System.out.println("使用Vga口");
    	}
    
    }
    
    

    测试

    package src.com.zzf.designpattern.adapterpattern.demo5;
    
    import src.com.zzf.designpattern.adapterpattern.demo4.IPortExchangeTarget;
    
    public class Test {
    	public static void main(String[] args) {
    		IPortExchangeTarget  portExchangeTarget = new ExchangeAdapter(new TypeCEqupAdaptee());
    		portExchangeTarget.useAsAndroid();
    		portExchangeTarget.useAsHdmi();
    		portExchangeTarget.useAsUsb();
    		portExchangeTarget.useAsVga();
    		
    		IPortExchangeTarget  portExchangeTarget2 = new ExchangeAdapter(new UsbEqupAdaptee());
    		portExchangeTarget2.useAsAndroid();
    		portExchangeTarget2.useAsHdmi();
    		portExchangeTarget2.useAsUsb();
    		portExchangeTarget2.useAsVga();
    	}
    }
    

    通过以上的案例可以看到,对象适配器其实就是通过委托不同的对象,从而实现不同转换的目的

    接口适配器

    接口适配器可能与上述的两种有点区别,其实接口适配器就是起到中间缓和的作用。举个例子,当我们需要实现的接口内部有过多的方法的时候,但是这个时候很多方法其实对我当前对象是没有任何意义的,是不需要重写的,那么此时我们可以使用一个抽象类,将不需要在子类中进行实现的对象过滤出来,这样在子类中,则就不会呈现出过多的方法。

    代码三

    定义一个很多方法的接口

    package src.com.zzf.designpattern.adapterpattern.demo6;
    
    public interface OriginInterFace {
    	void function1();
    	void function2();
    	void function3();
    	void function4();
    	void function5();
    	void function6();
    	void function7();
    	void function8();
    	void function9();
    	
    }
    
    

    定义一个中间适配器,过滤掉无用的方法

    package src.com.zzf.designpattern.adapterpattern.demo6;
    
    public  abstract class OriginInterFaceAdapter implements OriginInterFace{
    
    	@Override
    	public void function1() {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void function2() {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void function3() {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void function4() {
    		// TODO Auto-generated method stub
    		
    	}
    
    
    	@Override
    	public void function7() {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void function8() {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void function9() {
    		// TODO Auto-generated method stub
    		
    	}
    }
    
    

    实现出

    package src.com.zzf.designpattern.adapterpattern.demo6;
    
    public class Concrete extends OriginInterFaceAdapter{
    
    	@Override
    	public void function5() {
    		// TODO Auto-generated method stub
    		System.out.println("function5");
    	}
    
    	@Override
    	public void function6() {
    		// TODO Auto-generated method stub
    		System.out.println("function6");
    	}
    
    }
    
    

    接口适配器算是最简单的。只是加了一个缓冲层

    基于UML的代码

    ITarget接口

    package src.com.zzf.designpattern.adapterpattern.demo2;
    
    
    public interface ITarget {
    	void request();
    }
    
    

    具体的ConcreteTarget类

    package src.com.zzf.designpattern.adapterpattern.demo2;
    
    
    public class ConcreteTarget implements ITarget{
    
    	public void request() {
    		// TODO Auto-generated method stub
    		System.out.println("ConcreteTarget request");
    	}
    
    }
    
    

    需要做适配的对象Adaptee

    package src.com.zzf.designpattern.adapterpattern.demo2;
    
    public class Adaptee {
    	public void doSomeThing() {
    		System.out.println("Adaptee doSomething");
    	}
    }
    
    

    具体的适配器对象

    package src.com.zzf.designpattern.adapterpattern.demo2;
    
    
    public class Adapter extends Adaptee implements ITarget {
    
    	public void request() {
    		// TODO Auto-generated method stub
    		super.doSomeThing();
    	}
    
    }
    
    

    具体的适配器测试

    
    
    import java.lang.annotation.Target;
    /**
     * 适配器模式(Adapter Pattern)
     * Convert the interface of a class into annther interface clients except .Adapter lets classes work together that
     * couldn't otherwise because of incompatible interface;(将一个类的接口转换成客户所期待的另一种接口,从而使原本因接口不匹配而无法在
     * 一起工作的两个类能够在一起工作)
     * @author zhangfei.zhou
     * 适配器模式又叫做变压器模式,也叫做包装模式,但是包装模式并不止一种,还包括了装饰者模式
     */
    public class Test {
    	public static void main(String[] args) {
    		ITarget mTarget = new ConcreteTarget();
    		mTarget.request();
    		
    		ITarget mITarget2 = new Adapter();
    		mITarget2.request();
    	}
    }
    
    

    类适配器,对象适配器,接口适配器的区分

    类适配器主要是从继承的角度来进行适配器的设计,适配器作为被适配者的子类,自然拥有更多的操作空间,比如重写方法
    对象适配器则是使用组合的形式来进行适配器的设计,基于对象的适配器,可以有多种组合形态。
    接口适配器,其实完全是一种缓冲区域的设计,避免重写过多的方法而设计的。




    欢迎继续访问,我的博客
    展开全文
  • 目录 1. 前言 2. 适配器模式详解 2.1 适配器模式的定义 2.2 适配器模式的类结构 2.3 适配器模式的实现 2.4 适配器模式的使用场景 2.5 类适配器 3. 总结 ...
  • 什么是适配器模式?  适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。  Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 什么时候运用适配器模式?  在想使用一个...
  • (八)适配器模式详解  作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。  各位好,我们本次...
  • Java适配器模式详解

    2020-04-16 15:27:09
    文章目录设计模式分类适配器模式`实例:`小结编程之外 设计模式分类 设计模式分为三种类型,共23种:这里先暂时只分享适配器模式的学习 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。 ...
  • java适配器模式详解

    2019-08-23 15:42:15
    适配器模式主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。首先,我们来看看类的适配器模式,先看类图: 核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过...
  • 走穿23种设计模式-8适配器模式详解适配器模式,也叫变压器模式。在适配器模式下,变压器的作用是把一种电压变成另一种电压。 上面是用生活相关的词语解析。 其实适配器对我们Android程序员来说是非常非常熟悉的了...
  • 作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明...各位好,我们本次接着讨论第八个设计模式,适配器模式适配器模式从实现方式上分为两种,类适配器和对象适配器,这两种的区别在于实现方式上的不同...
  • **适配器模式** 适配器模式的官方定义:将一个类的接口转换为客户期望的另外的一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 目的:消除由于接口不匹配造成的类的不兼容问题。 分类:有...
  • 适配器模式:将一个类的结构转换成客户希望的另外一个借口。适配器模式使得原本由于借口不兼容而不能一起工作的那些类可以一起工作。在软件开发过程中,如果系统的数据和行为都正确,但借口不符时我们应该考虑适配器...
  • 文章目录适配器模式适配器模式适用场景类适配器对象适配器源码中的适配器适配不同格式的数据日志 适配器模式 适配器模式又叫变压器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得...
  • 设计模式——适配器模式详解

    千次阅读 2017-06-18 21:54:28
    适配器模式把一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不兼容而无法一起工作的两个类能一起工作。适配器模式的使用场景: (1)系统需要使用现有的类,而此类的接口不符合需要。 (2)需要一个...
  • 九:适配器模式详解

    2020-05-17 22:15:27
    适配器模式是指将一个类的接口转换成用户期望的一个接口,事原本接口不兼容的类可以一起工作,属于结构型设计模式 适配器模式适用于以下几种业务场景 1.已经存在的类 的方法和需求不匹配(方法结果相同或相似)的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 930
精华内容 372
关键字:

适配器模式详解