适配器模式

作者: 起个名忒难 | 来源:发表于2017-07-04 00:12 被阅读40次

    定义

    将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

    适配器模式又称为变压器模式,适配器在生活中也有广泛的应用的,例如,笔记本的电源适配器,电器的适配器等,笔记本或者电器的工作电压可能在几十伏,而国家电压是220伏,这样笔记本或者电器直接接在国家电压上是无法工作的,接一个适配器,将220伏降低到笔记本的工作电压(变压器),就能正常工作了。

    角色

    • 目标抽象类(Target):目标抽象类中定义了客户所需要的接口,可以是抽象类或者是一个接口,也可以是一个具体的类
    • 适配器类(Adapter):适配器类可以调用另一个接口,作为一个转换器,对目标抽象类和适配者类进行适配,从而使适配者类能够被客户端正常的调用。可以继承或者实现抽象者类,并关联一个适配者类。
    • 适配者类(Adeptee):被适配的角色,定义了一个已经存在的接口但是和目标抽象者类的接口并不兼容。但是它包含了客户希望使用的业务方法。

    适配器分为对象适配器和类适配器,类适配器和对象适配器之间最大的区别在于,类适配器模式中适配器和适配者之间是继承关系,而对象适配器模式中适配器和适配者之间是关联关系。

    public class Adapter extends Adaptee implements Target{
        //目标抽象类方法
        public void request(){  
            specificRequest(); //适配者类方法
        }
    }
    

    上面代码就是典型的类适配器模式,但是JAVA语言不支持多重继承,如果目标抽象类是一个类,而不是一个接口,就无法使用类适配器,因此类适配器很少使用,大部分使用的是对象适配器。

    业务描述

    公司为适应发展要开发一套新的会员系统,业务比较的简单,说干就干,看下面代码实现:

    会员信息接口
    public interface IUser {
        public String getUserName() ;  //获取会员姓名
        public String getAddress() ;   //获取会员地址
    }
    
    class User implements IUser {
    
        public String getUserName() {
            System.out.println("output user name !");
            return null;
        }
    
        public String getAddress() {
            System.out.println("output user address !");
            return null;
        }
    }
    
    //客户端调用
    public class Client {
        public static void main(String[] args) {
            IUser user = new User();
            user.getUserName();
            user.getAddress();
        }
    }
    

    程序到这看起来一切都很正常,项目也顺利的上线了,但是需求总是会和你不期而遇,业务部门提要求了,说操作两个系统很麻烦,能不能把老的会员系统和新的会员系统做关联,在新的系统中也能查看老的会员系统中的会员信息,由于老的会员系统已经沿用了很多年,还有其他的功能,做数据库迁移是在是太费劲了。看一下老会员系统中会员信息的类设计:

    interface IOldUser{
        Map getUserInfo();
    }
    
    class OldUser implements  IOldUser{
    
        public Map getUserInfo() {
            //模拟数据
            Map<String ,Object > map = new HashMap<>() ;
            map.put("name" ,"zhangsan") ;
            map.put("address", "beijing");
            return map;
        }
    }
    

    可以看出,老会员系统把会员信息放在了一个map中,和新会员系统完全是不同的设计,这个时候,该适配器类上场了。

    public class Adapter extends OldUser implements IUser {
        Map<String, Object> map = super.getUserInfo();
        @Override
        public String getUserName() {
            System.out.println(map.get("name").toString());
            return null;
        }
    
        @Override
        public String getAddress() {
            System.out.println(map.get("address").toString());
            return null;
        }
    }
    

    客户端调用:

    public class Client {
        public static void main(String[] args) {
            //新会员系统
            IUser user = new User();
            user.getUserName();
            user.getAddress();
            //老会员系统
            IUser user1 = new Adapter();
            user1.getAddress();
            user1.getUserName();
    
        }
    }
    

    对象适配器,典型代码实现:

    class Adapter implements Target{
        // 直接关联被适配类
        private Adaptee adaptee;
        
        // 可以通过构造函数传入具体需要适配的被适配类对象
        public Adapter (Adaptee adaptee) {
            this.adaptee = adaptee;
        }
        
        public void request() {
            // 这里是使用委托的方式完成特殊功能
            this.adaptee.specificRequest();
        }
    }
    

    对象适配器的使用方式比较简单,也是最常用的使用方式,这里不再赘述。直接将实现了目标抽象类接口类与被适配者类做关联即可。

    总结

    优点

    • 适配器模式让两个没有任何关系的类在一起运行
    • 增加了类的透明性,访问的还是Target角色,但是却委托给了Adeptee角色来运行
    • 提高了类的复用程度,灵活性很好

    缺点

    • 对于不支持多重继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
    • 适配者类不能为最终类,被final修饰的类无法使用
    • 目标抽象类只能是接口,不能为类

    适用场景

    • 系统中需要使用一些已有的类,但这些类的接口不符合系统的需要
    • 像创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的类


    少年听雨歌楼上,红烛昏罗帐。  
    壮年听雨客舟中,江阔云低,断雁叫西风。
    感谢支持!
                                            ---起个名忒难
    

    相关文章

      网友评论

        本文标题:适配器模式

        本文链接:https://www.haomeiwen.com/subject/cmcthxtx.html