07-电源转换:适配器模式
第二部分:结构型模式
7.1 生活中的适配器
在生活当中的很多适配器的例子,如插座转换器、笔记本电脑电源适配器等。现在,我们就来看下笔记本电脑电源适配器的例子。通常,笔记本电脑需要的电流都是12~20v以内的直流电流,而我们家庭用电通常是220v的交流电,当然还有工业用电380v交流电,还有更低的110v交流电源。如何把这些不同的交流电源转换成笔记本电脑需要的直流电呢?这就需要使用电源适配器。我们来看下面的例子,如图7-1所示:
从上图我们看到,笔记本电脑电源适配器可以将不同的交流电源转换成计算机需要的12v直流电,而自身不需要任何的改变就可以达到这样的功能,这个适配器就是交流电源适配器——AC Adapter。AC Adapter的作用就是介入既有的内容(各种已经存在的交流电源)和需要的结果(12V直流电),作为彼此之间沟通的一个桥梁。
在软件设计中,也存在适配器的需求。既有的软件结构具有稳定运行的基础,但是无法直接利用到新的程序当中,这是就需要一个适配器,在原有内容和新的结果之间沟通,从而达到预期的效果。
7.2模式定义
适配器模式(Adapter Pattern),把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作,又称为转换器模式、变压器模式、包装(Wrapper)模式(把已有的一些类包装起来,使之有能满足需要的接口)。
存在两种适配器模式:
1)对象适配器模式——在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。
2)类适配器模式——在这种适配器模式下,适配器继承自己实现的类。
无论哪种适配器,它的宗旨都是:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。即在不改变原有系统的基础上,提供新的接口服务。
7.3 模式分析
7.3.1适配器中的角色
首先来分析下笔记本电脑电源适配器的例子:
有以下几个角色:
1)已经存在的交流电源;
2)笔记本电脑;
3)电源适配器。
我们的目标是:将220v的交流电源转换成笔记本电脑需要的12v直流电。
7.3.2 静态建模
从图7-3中可以看到,电源适配器类AdapterPower12实现了IPower12接口(我们所期望得到的目标结果),使用对象适配器模式,通过构造方法将外部的电源类传入适配器中,然后经过转换输出我们期望的结果。
7.4 模式实现
7.4.1 抽象电源建立
package com.demo.power; /** * Created by lsq on 2018/3/16. * 电源基类 */ public abstract class AbstractBasePower { //电压值 private float power; //单位 private String unit = "V"; //构造方法 public AbstractBasePower(float power) { this.power = power; } public float getPower() { return power; } public void setPower(float power) { this.power = power; } public String getUnit() { return unit; } public void setUnit(String unit) { this.unit = unit; } }
7.4.2 创建220v电源
1 220v电源接口——IPower220
package com.demo.power.v220; /** * Created by lsq on 2018/3/16. * 220v 电源接口 */ public interface IPower220 { //220v交流电源打印 public void output220v(); }
2. 220v电源实现类——Power220
package com.demo.power.v220; import com.demo.power.AbstractBasePower; /** * Created by lsq on 2018/3/16. * 220v 电源实现类 */ public class Power220 extends AbstractBasePower implements IPower220{ //构造方法 public Power220(){ super(220); } //220v电源输出 public void output220v() { System.out.println("---这是["+this.getPower()+this.getUnit()+"]电源!---"); } }
7.4.3 创建12v电源
1. 12v电源接口——IPower12
package com.demo.power.v12; /** * Created by lsq on 2018/3/16. * 12v 电源接口 */ public interface IPower12 { //12v交流电源打印 public void output12v(); }
2. 12v电源实现类——Power12
package com.demo.power.v12; import com.demo.power.AbstractBasePower; /** * Created by lsq on 2018/3/16. * 220v 电源实现类 */ public class Power12 extends AbstractBasePower implements IPower12 { //构造方法 public Power12(){ super(12); } //12v电源输出 public void output12v() { System.out.println("---这是["+this.getPower()+this.getUnit()+"]电源!---"); } }
7.4.4 测试220v电源和12v电源
新建客户端应用程序Client,内容如下:
package com.demo; import com.demo.power.v12.IPower12; import com.demo.power.v12.Power12; import com.demo.power.v220.Power220; /** * Created by lsq on 2018/3/16. * 客户端程序调用 */ public class Client { public static void main(String[] args) { //首先生成一个220v电源对象 Power220 power220 = new Power220(); power220.output220v(); //再生成一个12v电源对象 IPower12 power12 = new Power12(); power12.output12v(); } }
运行结果:
7.4.5 对象适配器实现
1. 12v电源对象适配器——AdapterPower12
新建“AdapterPower12”电源适配器类,该类实现12v电源接口。使用对象适配器模式,通过构造方法传入外部电源实例,经过转换,输出我们需要的12v电源。
package com.demo.adapter; import com.demo.power.AbstractBasePower; import com.demo.power.v12.IPower12; /** * Created by lsq on 2018/3/16. * 电源适配器(实现目标对象接口,即12v电源接口) */ public class AdapterPower12 implements IPower12 { //待转换对象 private final AbstractBasePower abstractBasePower; //适配器构造方法,将待转换对象传入 public AdapterPower12(AbstractBasePower abstractBasePower){ this.abstractBasePower = abstractBasePower; } //实现目标对象方法 public void output12v() { //获得外部电源值 float powerFloat = this.abstractBasePower.getPower(); //进行电源转换 if (powerFloat == 380){ //380v电源转换 powerFloat = powerFloat/31.67f; }else if (powerFloat == 220){ //220v电源转换 powerFloat = powerFloat/18.33f; }else if (powerFloat == 110){ //110v电源转换 powerFloat = powerFloat/9.17f; }else { System.out.println("---不能适配电源!---"); return; } //处理转换结果 powerFloat = (int)(powerFloat * 10)/10.0f; System.out.println("---这是["+powerFloat+this.abstractBasePower.getUnit()+"]电源!---"); } }
2. 测试对象适配器
package com.demo; import com.demo.adapter.AdapterPower12; import com.demo.power.v12.IPower12; import com.demo.power.v12.Power12; import com.demo.power.v220.Power220; /** * Created by lsq on 2018/3/16. * 客户端程序 */ public class Client2 { public static void main(String[] args) { //首先生成一个220v电源对象 Power220 power220 = new Power220(); power220.output220v(); //生成一个12v电源对象 IPower12 power12 = new Power12(); power12.output12v(); //使用电源适配器将220v电源转换成12v电源 System.out.println("\n---电源适配器转换中!---"); IPower12 adapterPower12 = new AdapterPower12(power220); adapterPower12.output12v(); System.out.println("---电源适配器转换结束!---"); } }
运行结果:
从结果中看出,现在即使没有12v的正常电源,我们使用一个电源适配器和220v的电源就可以达到12v电源的效果。外部程序根本不知道,这是正常的12v电源还是转换得到的12v电源。
7.4.6 类适配器模式实现
上面使用的是对象适配器的方法,还有一种是通过类适配器的方法实现。类适配器和对象适配器是外部对象和适配器的一种关联关系,类适配器使用继承的方式,它是一种强关联关系,而对象适配器是利用组合的方式,将外部对象传入,它是一种弱关联关系。下面一起来实现类适配器模式。
1. 12v电源类适配器——AdapterPower12Ext
package com.demo.adapter; import com.demo.power.AbstractBasePower; import com.demo.power.v12.IPower12; /** * Created by lsq on 2018/3/16. * 电源适配器(实现目标对象接口,即12v电源接口),类适配器模式 */ public class AdapterPower12Ext extends AbstractBasePower implements IPower12{ //适配器构造方法,将待转换对象转入 public AdapterPower12Ext(AbstractBasePower abstractBasePower) { super(abstractBasePower.getPower()); } //实现目标方法 public void output12v() { //获得外部电源值 float powerFloat = this.getPower(); //进行电源转换 if (powerFloat == 380){ //380v电源转换 powerFloat = powerFloat/31.67f; }else if (powerFloat == 220){ //220v电源转换 powerFloat = powerFloat/18.33f; }else if (powerFloat == 110){ //110v电源转换 powerFloat = powerFloat/9.17f; }else { System.out.println("---不能适配电源!---"); return; } //处理转换结果 powerFloat = (int)(powerFloat * 10)/10.0f; System.out.println("---这是["+powerFloat+this.getUnit()+"]电源!---"); } }
注意:类适配器和对象适配器的不同之处就在于,类适配器使用继承方法,而对象适配器使用组合的方式。类适配器继承被转换类,将传入的外部应用变为自身类的内容,然后对自身内容转换获得目标结果。
2. 测试类适配器
package com.demo; import com.demo.adapter.AdapterPower12; import com.demo.adapter.AdapterPower12Ext; import com.demo.power.v12.IPower12; import com.demo.power.v12.Power12; import com.demo.power.v220.Power220; /** * Created by lsq on 2018/3/16. * 客户端程序 */ public class Client3 { public static void main(String[] args) { //首先生成一个220v电源对象 Power220 power220 = new Power220(); power220.output220v(); //生成一个12v电源对象 IPower12 power12 = new Power12(); power12.output12v(); //使用电源适配器将220v电源转换成12v电源 System.out.println("\n---电源适配器转换中!---"); IPower12 adapterPower12 = new AdapterPower12(power220); adapterPower12.output12v(); System.out.println("---电源适配器转换结束!---"); //类适配器实现 System.out.println("\n---类适配器 电源适配器转换中!---"); IPower12 adapterPower12Ext = new AdapterPower12Ext(power220); adapterPower12Ext.output12v(); System.out.println("---类适配器 电源适配器转换结束!---"); } }
运行结果:
7.5 使用场合
1)软件系统结构需要升级或扩展,又不想影响原有系统的稳定运行的时候;
2)转换类之间的差别不是太大的时候;
3)想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作的时候。
适配器模式主要用于系统的升级扩展,或者版本兼容性上,没有哪一个系统分析师会在软件设计阶段使用适配器模式的。适配器模式可以很好地解决版本兼容问题。另外,就是原有类和新的接口标准不能差别太大,否则,适配器模式是达不到预期效果的。适配器模式在软件的后期维护中具有很大的优势,它不仅能保持原有系统的稳定,而且还能进行新功能的扩展,非常灵活、实用。
扩展1:适配器模式与桥接模式
适配器模式是将一种接口转换为另外一种接口的设计模式,接下来要学习的桥接模式则是连接功能部分和实现部分的设计模式,桥接模式的用意是要把实现和它的接口分开,以便它们可以独立的变化。桥接模式并不是用来把一个已有的对象接到不相匹配的接口上的,两者是有区别的。
扩展2:适配器模式与装饰者模式
适配器模式主要用来填补两个接口之间的差异,而装饰者模式,则是不需要更改接口,就可以新增功能的设计模式。