YAZONG 我的开源

设计模式(七)结构型模式(类对象结构型模式)适配器模式-ADAPTER

 
0 评论0 浏览

2、7适配器模式

定义

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

适配器模式又叫做变压器模式,也叫做包装模式,但是包装模式可不止一个,比如装饰模式等。

类适配器

通用类图

image.png

案例

/**
 *场景类
 */

public class Client1 {

    public static void main(String[] args) {

        //原有的业务逻辑

        Target target = new ConcreteTarget();

        target.request();

        //现在增加了适配器角色后的业务逻辑

        Target target2 = new Adapter();

        target2.request();

    }

}

 

/**

 * Target目标角色

 * 该角色定义把其他类转换为何种接口,也就是我们的期望接口。

 * 目标角色是一个已经正式运行的角色,我们不可能去修改角色中的方法,我们能做的就是如何去实现接口中的方法,

 * 而且通常情况下,目标角色是一个接口或者是抽象类,一般不会是实现类。

 */

interface Target{

    //目标角色有自己的方法

    void request();

}

 

/**

 * 目标角色的实现类

 */

class ConcreteTarget implements Target{

    @Override

    public void request() {

        System.out.println("ConcreteTarget目标角色的实现类");

    }

}

 

/**

 * 源角色

 */

class Adaptee{

    //原有的业务逻辑

    public void doSomething(){

        System.out.println("Adaptee源角色原有的业务逻辑");

    }

}

 

/**

 * 适配器角色

 */

class Adapter extends Adaptee implements Target{

    @Override

    public void request() {

        super.doSomething();

    }

}

对象适配器

通用类图

image.png

案例

图形结构

image.png

代码逻辑
/**
 *场景类
 */
public class Client2 {
    public static void main(String[] args) {
        //外系统的人员信息
        IOuterUserBaseInfo baseInfo = new OuterUserBaseInfo();
        IOuterUserHomeInfo homeInfo = new OuterUserHomeInfo();
        IOuterUserOfficeInfo officeInfo = new OuterUserOfficeInfo();

        //传递对象
        IUserInfo userInfo = new OuterUserInfo(baseInfo,homeInfo,officeInfo);
        userInfo.getMobileNumber();

    }
}

/**
 * 用户基本信息接口
 */
interface IOuterUserBaseInfo{
    //基本信息:比如名称、性别、手机号码等。
    Map getUserBaseInfo();
}
/**
 * 用户基本信息
 */
class OuterUserBaseInfo implements IOuterUserBaseInfo{
    @Override
    public Map getUserBaseInfo() {
        Map map = new HashMap();
        map.put("userName","这个员工叫混世魔王");
        map.put("mobileNumber","这个员工电话是....");
        return map;
    }
}
/**
 * 用户家庭信息接口
 */
interface IOuterUserHomeInfo{
    //用户的家庭信息
    Map getUserHomeInfo();
}
/**
 * 用户家庭信息
 */
class OuterUserHomeInfo implements IOuterUserHomeInfo{
    @Override
    public Map getUserHomeInfo() {
        Map map = new HashMap();
        map.put("homeTelNumber","这个员工的家庭电话是..");
        map.put("homeAddress","这个员工的家庭地址是....");
        return map;
    }
}
/**
 * 用户工作信息接口
 */
interface IOuterUserOfficeInfo{
    //工作区域信息
    Map getUserOfficeInfo();
}
class OuterUserOfficeInfo implements IOuterUserOfficeInfo{
    @Override
    public Map getUserOfficeInfo() {
        Map map = new HashMap();
        map.put("jobPosition","这个员工的职位是..");
        map.put("officeTelNumber","这个员工的办公电话是....");
        return map;
    }
}

interface IUserInfo{
    String getUserName();
    String getHomeAddress();
    String getMobileNumber();
    String getOfficeTelNumber();
    String getJobPosition();
    String getHomeTelNumber();
}
class UserInfo implements IUserInfo{
    @Override
    public String getUserName() {
        System.out.println("员工姓名");
        return null;
    }

    @Override
    public String getHomeAddress() {
        System.out.println("员工家庭地址");
        return null;
    }

    @Override
    public String getMobileNumber() {
        System.out.println("员工手机号码");
        return null;
    }

    @Override
    public String getOfficeTelNumber() {
        System.out.println("员工办公电话");
        return null;
    }

    @Override
    public String getJobPosition() {
        System.out.println("员工职位");
        return null;
    }

    @Override
    public String getHomeTelNumber() {
        System.out.println("员工家庭电话");
        return null;
    }
}

/**
 * 适配器
 */
class OuterUserInfo implements IUserInfo{

    //源目标对象
    private IOuterUserBaseInfo baseInfo;
    private IOuterUserHomeInfo homeInfo;
    private IOuterUserOfficeInfo officeInfo;

    //数据处理
    private Map baseMap;
    private Map homeMap;
    private Map officeMap;

    public OuterUserInfo(IOuterUserBaseInfo _baseInfo, IOuterUserHomeInfo _homeInfo, IOuterUserOfficeInfo _officeInfo) {
        this.baseInfo = _baseInfo;
        this.homeInfo = _homeInfo;
        this.officeInfo = _officeInfo;

        this.baseMap = this.baseInfo.getUserBaseInfo();
        this.homeMap = this.homeInfo.getUserHomeInfo();
        this.officeMap = this.officeInfo.getUserOfficeInfo();

    }

    @Override
    public String getUserName() {
        String val = (String)this.baseMap.get("userName");
        System.out.println(val);
        return val;
    }
    @Override
    public String getMobileNumber() {
        String val = (String)this.baseMap.get("mobileNumber");
        System.out.println(val);
        return val;
    }
    @Override
    public String getHomeAddress() {
        String val = (String)this.homeMap.get("homeAddress");
        System.out.println(val);
        return val;
    }

    @Override
    public String getHomeTelNumber() {
        String val = (String)this.homeMap.get("homeTelNumber");
        System.out.println(val);
        return val;
    }

    @Override
    public String getOfficeTelNumber() {
        String val = (String)this.officeMap.get("officeTelNumber");
        System.out.println(val);
        return val;
    }

    @Override
    public String getJobPosition() {
        String val = (String)this.officeMap.get("jobPosition");
        System.out.println(val);
        return val;
    }


}

调用过程

1)客户通过目标接口调用适配器的方法对适配器发出请求。

2)适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。

3)客户接收到调用的结果,但并未察觉这一切是适配器在起转换作用(客户和被适配者是解耦的,一个不知道另一个)。

类适配器和对象适配器区别

类适配器是类间继承,对象适配器是对象的合成/组合关系,也可以说是类的关联关系,这是两者的根本区别。这两种实现的差异影响了适配器的弹性

二者在实际项目中都会经常用到,由于对象适配器是通过类间的关联关系进行耦合的,因此在设计时就可以做到比较灵活,比如修补源角色的隐形缺陷,关联其他对象等,而类适配器就只能通过覆写源角色的方法进行扩展,在实际项目中,对象适配器使用到场景相对较多。

优点

1)适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就行。

2)增加了类的透明性。想想看,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。

3)提高了类的复用度。当然了,源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。

4)灵活性非常好。某一天,突然不想要适配器,没问题,删除掉这个适配器就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不想用就卸载。

5)被适配者的任何子类,都可以搭配着适配器使用。

6)适配器模式是把客户和接口绑定起来,而不是和实现绑定起来。可以使用数个适配器,每一个都负责转换不同组的后台类。或者,也可以加上新的实现,只要它们遵守目标接口就可以。

使用场景

1)系统的数据和行为都正确,但接口不符时(有动机修改一个已经投产中的接口时),此时应该考虑使用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。

比如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接口。

比如在需要对早期代码复用一些功能等应用上很有实际价值。

2)双方都不太容易修改的时候再使用适配器模式,而不是一有不同时就使用它。

比如在设计之初接入第三方接口时,没有必要为了迎合第三方而改动自己的接口,也可以考虑使用适配器模式来解决接口不同的问题。

注意事项

1)适配器模式最好在详细设计阶段不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题,没有一个系统分析师会在做详细设计的时候考虑使用适配器模式,这个模式使用的场景是扩展应用中,系统扩展了,不符合原有设计的时候才考虑通过适配器模式减少代修改带来的风险。

2)再次提醒一点,项目一定要遵守依赖倒置原则和里式替换原则,否则即使在适合使用适配器的场合下,也会带来非常大的改造。

3)如果能事先预防接口不同的问题,不匹配问题就不会发生;在有小的接口不统一问题发生时,及时重构,问题不至于扩大;只有碰到无法改变原有设计和代码的情况时,才考虑适配。事后控制不如事中控制,事中控制不如事前控制。


标题:设计模式(七)结构型模式(类对象结构型模式)适配器模式-ADAPTER
作者:yazong
地址:https://blog.llyweb.com/articles/2020/06/11/1591805175130.html