GOF设计模式-对象结构型模式-适配器模式

26 篇文章 0 订阅
20 篇文章 0 订阅

不兼容结构的协调—适配器模式

我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够 在220V的电压下工作?答案是引入一个电源适配器(AC Adapter),俗称充电器或变压器,有了 这个电源适配器,生活用电和笔记本电脑即可兼容

在软件开发中,有时也存在类似这种不兼容的情况,我们也可以像引入一个电源适配器一样 引入一个称之为适配器的角色来协调这些存在不兼容的结构,这种设计方案即为适配器模 式。

适配器模式可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口 和抽象目标类接口。适配器模式定义如下:
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那 些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可 以作为对象结构型模式。

 

适配器模式分类:

 

  • 类适配器(通过引用适配者进行   继承 实现)
  • 对象适配器(通过  组合 适配者进行实现)
  • 接口适配器(通过 抽象类 来实现适配器)

 

1.类适配器模式

当我们要访问的接口A中没有我们想要的方法 ,却在另一个接口B中发现了合适的方法,我们又不能改变访问接口A,在这种情况下,我们可以定义一个适配器p来进行中转,这个适配器p要实现我们访问的接口A,这样我们就能继续访问当前接口A中的方法(虽然它目前不是我们的菜),然后再继承接口B的实现类,这样我们可以在适配器P中访问接口B的方法了,这时我们在适配器P中的接口A方法中直接引用B中的合适方法,这样就完成了一个简单的类适配器。

举个栗子:通过把普通登录的接口转换成微信登录的接口

登录返回状态的类

/**
 * @Project: spring
 * @description: 登录返回的结果的类
 * @author: sunkang
 * @create: 2018-09-05 20:55
 * @ModificationHistory who      when       What
 **/
public class ResultMsg {
    private  String code;
    private  String msg;
    private  Object data;
    public ResultMsg(String code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
}

普通登录接口:

/**
 * @Project: spring
 * @description:   登录的接口业务
 * @author: sunkang
 * @create: 2018-09-05 20:51
 * @ModificationHistory who      when       What
 **/
public interface ISiginSerevice {
    ResultMsg login(String username,String password);
}

登录的具体实现:

* @Description:
* @Param: 登录的具体实现
* @return:
* @Author: sunkang
* @Date: 2018/9/5
*/
public class SiginService implements ISiginSerevice {
    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username,String password){
        return  new ResultMsg("200","登录成功",new Object());
    }
}

通过微信接口登录:

/**
 * @Project: spring
 * @description:  通过微信接口登录
 * @author: sunkang
 * @create: 2018-09-05 20:58
 * @ModificationHistory who      when       What
 **/
public interface ISiginForWebChat {
    /**
     * 通过微信登录
     * @param openId
     * @return
     */
     ResultMsg loginForWechat(String openId);
}

类适配器(把原有的登录方法适配成微信登录的接口)

/**
 * @Project: spring
 * @description:  类适配器   目标接口为ISiginForWebChat,但是适配者的接口为ISiginService
 * @author: sunkang
 * @create: 2018-09-05 21:29
 * @ModificationHistory who      when       What
 **/
public class ClassSiginForWebChatAdapter  extends  SiginService implements  ISiginForWebChat{
    @Override
    public ResultMsg loginForWechat(String openId) {
        return login(openId,null);
    }
}

2.对象适配器模式:

 

原理:通过组合来实现适配器功能。

由于比较简单,应该是比较清楚的,代码如下:

/**
 * @Project: spring
 * @description:  对象适配器  默认通过组合来实现的
 * @author: sunkang
 * @create: 2018-09-05 21:06
 * @ModificationHistory who      when       What
 **/
public class ObjectSiginForWebChatAdapter implements ISiginForWebChat {
    private ISiginSerevice siginSerevice;
    public ObjectSiginForWebChatAdapter(ISiginSerevice siginSerevice) {
        this.siginSerevice = siginSerevice;
    }
    @Override
    public ResultMsg loginForWechat(String openId) {
        return siginSerevice.login(openId,null);
    }
}

3.接口适配器模式:也叫缺省适配器

 

原理:通过抽象类来实现适配,这种适配稍别于上面所述的适配。

当存在这样一个接口,其中定义了N多的方法,而我们现在却只想使用其中的一个到几个方法,如果我们直接实现接口,那么我们要对所有的方法进行实现,哪怕我们仅仅是对不需要的方法进行置空(只写一对大括号,不做具体方法实现)也会导致这个类变得臃肿,调用也不方便,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们在创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。

比如现在登录要支持第三方接口,比如通过qq登录,通过微信登录,通过手机号和验证码登录,但是有的实现类不是通通用到这些方法,可以用一个抽象类来实现接口的所有的方法,作为一个默认的实现,然后具体的实现类继承抽>象类,要用到哪个方法就重写哪个方法

第三方登录的接口:

 
/**
 * @Project: spring
 * @description:   第三方登录的接口
 * @author: sunkang
 * @create: 2018-09-05 21:41
 * @ModificationHistory who      when       What
 **/
public interface ISiginForThirdService {
    /**
     * 通过qq登录
     * @param openId
     * @return
     */
     ResultMsg loginForQQ(String openId);
    /**
     * 通过微信登录
     * @param openId
     * @return
     */
     ResultMsg loginForWechat(String openId);
    /**
     * 通过手机号和验证码登录
     * @param telphone
     * @param code
     * @return
     */
     ResultMsg loginForTelphone(String telphone,String code);
}

抽象适配器:

/**
 * @Project: spring
 * @description:   抽象适配器   因为接口太多,所以用了一个抽象的类的适配器,来默认实现
 * @author: sunkang
 * @create: 2018-09-05 21:43
 * @ModificationHistory who      when       What
 **/
public abstract class SiginForThirdAdapter implements  ISiginForThirdService {
    @Override
    public ResultMsg loginForQQ(String openId) {
        return new ResultMsg("200","qq登录成功",new Object());
    }
    @Override
    public ResultMsg loginForWechat(String openId) {
        return new ResultMsg("200","微信登录成功",new Object());
    }
    @Override
    public ResultMsg loginForTelphone(String telphone, String code) {
        return new ResultMsg("200","手机登录成功",new Object());
    }
}

通过电话号码和动态密码登录,只需要重写对应的方法接口

/**
 * @Project: spring
 * @description:  只需要用电话号码登录
 * @author: sunkang
 * @create: 2018-09-05 21:44
 * @ModificationHistory who      when       What
 **/
public class LoginForTelphoneAdapter extends SiginForThirdAdapter {
    public ResultMsg loginForTelphone(String telphone, String code) {
        System.out.println("通过电话号码登录");
        ResultMsg msg = new ResultMsg("200","qq登录成功",new Object());
        System.out.println("登录的结果为"+msg.getMsg());
        return msg;
    }
}

三种方式的测试案例

/**
 * @Project: spring
 * @description:  三种方式实现的测试案例
 * @author: sunkang
 * @create: 2018-09-05 21:17
 * @ModificationHistory who      when       What
 **/
public class AdapterTest {
    public static void main(String[] args) {
        //需要适配的类,要把  ISiginSerevice 变成 ISiginForWebChat
        ISiginSerevice siginSerevice  = new SiginService();
        ISiginForWebChat siginForThreadService  = new ObjectSiginForWebChatAdapter(siginSerevice);
        //对象适配器 ,传的是一个适配的类,通过组合的方式
        ResultMsg resultMsg =   siginForThreadService.loginForWechat("1231321ffasfasffad");
        System.out.println(resultMsg.getMsg());
        //类适配器,通过继承实现的
        ResultMsg msg =  new ClassSiginForWebChatAdapter().loginForWechat("1231321ffasfasffad");
        System.out.println(msg.getMsg());
        //接口适配器 ,主要实现了一个抽象的适配器
        //但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口
        //并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器
        ISiginForThirdService siginForWebChat = new LoginForTelphoneAdapter();
        ResultMsg telPoneMsg =  siginForWebChat.loginForTelphone("13588304966","1234");
        System.out.println(telPoneMsg.getMsg());
    }
}

适配器模式总结

适配器模式将现有接口转化为客户类所期望的接口,实现了对现有类的复用,它是一种使用 频率非常高的设计模式,在软件开发中得以广泛应用,在Spring等开源框架、驱动程序设计 (如JDBC中的数据库驱动程序)中也使用了适配器模式。

1. 主要优点
无论是对象适配器模式还是类适配器模式都具有如下优点:
(1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有 结构。
(2) 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而 言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
(3) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修 改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
具体来说,类适配器模式还有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配 器的灵活性更强。
对象适配器模式还有如下优点:
(1) 一个对象适配器可以把多个不同的适配者适配到同一个目标;
(2) 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原 则”,适配者的子类也可通过该适配器进行适配。
1. 主要缺点
类适配器模式的缺点如下:
(1) 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适 配多个适配者;
(2) 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
(3) 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一 定的局限性。
对象适配器模式的缺点如下:
与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉 适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然 后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
1. 适用场景
在以下情况下可以考虑使用适配器模式:
(1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有 这些类的源代码。
(2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可 能在将来引进的类一起工作。
 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值