最近一直在重温Java的那些经典设计模式,今天刚好看到“适配器模式”。
百度百科对适配器一词的解释:
适配器是英语Adapter/adaptor的汉语翻译。适配器就是一个接口 转换器 ,它可以是一个独立的硬件接口设备,允许硬件或电子接口与其它硬件或电子接口相连,也可以是信息接口。比如:电源适配器、三角架基座转接部件、USB与 串口 的转接设备等。
Java的适配器模式中的适配器,也是起到了一个转换的作用,将目前手头有单不匹配的功能接口转换成适用的目标接口。
更专业的解释是: 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
详细的解释参考网友博客 http://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html
Adaptee和Target接口中存在同名方法,容易让人产生一个误解: 待适配的接口和目标接口必须要有方法同名吗?
看了网上的一些关于适配器模式的解读,以及百度对适配器的解释,我产生了一些自己的想法。
我先根据我们最常接触的电源适配器来进行抽象,应用下适配器模式。
首先定义一个供电电源接口类PowerSource.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 * 电源接口 */ public interface PowerSource { public int supplyPower(); }
定义一个需要使用电源进行工作的笔记本电脑接口类Computer.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public interface Computer { public boolean startUp(); }
定义一个对应笔记本电脑的虚拟类,实现了Computer类的接口,增加了电压校验的方法AbstractComputer.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public abstract class AbstractComputer implements Computer { //具体电脑的目标电压 public abstract int getTargetVoltage(); /** * 电脑的启动入口 * @param source * @return * @throws PowerVoltageException */ public void startUp(PowerSource source) throws PowerVoltageException { //启动之前,先检查电压,如果是符合要求的稳定电压,则开始启动 if(checkPowerVoltage(source.supplyPower())) { //具体电脑的启动流程 startUp(); } } //电压检查,当前电压和电脑目标电压 public boolean checkPowerVoltage(int powerVoltage) throws PowerVoltageException { if(getTargetVoltage() == powerVoltage) { return true; } throw new PowerVoltageException("dangerous voltage for computer[voltage=220V]!! you may need a power adapter!"); } }
接下来分别定义了在两种电压下正常工作的具体电脑,220V电脑Computer220V.java,210V电脑Computer110V.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public class Computer220V extends AbstractComputer implements Computer { //目标电压是220V的电脑 private int startUpVoltage = 220; @Override public boolean startUp() { System.out.println("computer[voltage=220V] is starting!!"); System.out.println("[BIOS]check CPU......"); System.out.println("[BIOS]check Disk......"); System.out.println("[BIOS]check Software......"); System.out.println("[BIOS]check Memory......"); return true; } @Override public int getTargetVoltage() { return startUpVoltage; } }
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public class Computer110V extends AbstractComputer implements Computer { //目标电压是110V的电脑 private int startUpVoltage = 110; @Override public boolean startUp() { System.out.println("computer[voltage=110V] is starting!!"); System.out.println("[BIOS]check CPU......"); System.out.println("[BIOS]check Disk......"); System.out.println("[BIOS]check Software......"); System.out.println("[BIOS]check Memory......"); return true; } @Override public int getTargetVoltage() { return startUpVoltage; } }
然后实现一个电源,提供220V的电压PowerSource220V.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public class PowerSource220V implements PowerSource { @Override public int supplyPower() { //提供220V电压 return 220; } }
那么,到此我们来试试220V工况下,220V的电脑能否正常开机:
//创建一个220V电源 PowerSource powerSource220 = new PowerSource220V(); //启动220V的电脑 AbstractComputer computer = new Computer220V(); computer.startUp(powerSource220); AbstractComputer computer110 = new Computer110V(); try { //由于没有110V的适配器,这里110V的电脑启动失败 computer110.startUp(powerSource220); } catch (Exception e) { //错误信息打印 e.printStackTrace(); }
执行结果如下:
computer[voltage=220V] is starting!!
[BIOS]check CPU......
[BIOS]check Disk......
[BIOS]check Software......
[BIOS]check Memory......
designpatterns.adapter.e3.PowerVoltageException: dangerous voltage for computer[voltage=220V]!! you may need a power adapter!
at designpatterns.adapter.e3.AbstractComputer.checkPowerVoltage(AbstractComputer.java:32)
at designpatterns.adapter.e3.AbstractComputer.startUp(AbstractComputer.java:20)
at designpatterns.adapter.e3.App.main(App.java:20)
跟我们设想的一样,220V的电脑可以正常工作,但是110V的电脑无法开机,并提示错误。
在只有一个固定220V电源的情况下,我们如何让110V的电脑正常工作?答案就是通过一个适配器,将现有的220V电压进行转换,转为目标的110VPowerSourceAdapter.java:
package designpatterns.adapter.e3; /** * * @author Smile.Wu * @version 2015-10-19 */ public class PowerSourceAdapter extends PowerSource220V implements PowerSource { private int targetVoltage = 110; public PowerSourceAdapter(int targetVoltage) { super(); this.targetVoltage = targetVoltage; } @Override public int supplyPower() { //现有电源的电压 int sourceVoltage = super.supplyPower(); //如果当前的电源电压不是目标电压,则进行变压适配 if(sourceVoltage != targetVoltage) { //电压适配:变压 sourceVoltage = targetVoltage; } return sourceVoltage; } }
PowerSourceAdapter.java类继承了原有的电源,使自己具备了电源提供能力,然后实现目标接口PowerSource.java(这里目标接口其实没变,之所以还是实现了它,是为了更好的说明下面一个例子)。这样,在PowerSourceAdapter.java中就可以利用当前仅有的电源提供的电压,进行一个降压,然后输出。
我们实验下:
AbstractComputer computer110 = new Computer110V(); //创建110V的电源适配器 PowerSource powerSourceAdapterFor110V = new PowerSourceAdapter(110); //通过适配器的作用,成功启动110V的电脑 computer110.startUp(powerSourceAdapterFor110V);
输出如下:
computer[voltage=110V] is starting!!
[BIOS]check CPU......
[BIOS]check Disk......
[BIOS]check Software......
[BIOS]check Memory......
经过适配器的工作,110V的电脑也正常开机。
上面这个例子其实和真正的适配器模式没啥关系,因为待适配的接口和目标接口根本就是同一个。不过由此来进行一个过渡还是可以的。
在目前只有220V电源的情况下,我们要给手机充电,但是手机的充电接口和电脑完全不同,因此我定义了一个手机充电电源接口,低压供电电源LowPowerSource.java:
package designpatterns.adapter.e3; public interface LowPowerSource { public int getChargeSupply(); }
定义一个手机接口Phone.java,里面只有一个方法:充电。
package designpatterns.adapter.e3; public interface Phone { public void charge(LowPowerSource powerSource); }
然后我们获得一个手机ApplePhone.java实现了Phone.java的充电接口,参数就是低压供电电源LowPowerSource.java :
package designpatterns.adapter.e3; public class ApplePhone implements Phone { @Override public void charge(LowPowerSource powerSource) { if(powerSource.getChargeSupply() == 36) { System.out.println("apple phone is in charge!"); } else { throw new RuntimeException("voltage error of power source!"); } } }
由定义可知,目前我们的手机需要的是36V电压,而当前电源是220V,和上面的思路一样,我们需要一个适配器进行降压即可。
待适配的接口就是PowerSource.java,目标接口就是LowPowerSource.java:
package designpatterns.adapter.e3; public class ApplePowerSourceAdapater extends PowerSource220V implements LowPowerSource { @Override public int getChargeSupply() { //实现目标接口 //获得待适配的接口数据 int powerSource220 = super.supplyPower(); //进行一个适配处理 powerSource220 = 36; return powerSource220; } }
接下来再试验下:
ApplePhone phone = new ApplePhone(); phone.charge(new ApplePowerSourceAdapater());
结果如下:
apple phone is in charge!
实验成功,说明我们的思路是对的。
不过,适配器并没有跟待适配接口直接发生联系,而且继承了一个已经实现待适配接口的具体类,然后实现了目标接口。其实我们还可以这样做:
package designpatterns.adapter.e3; public class ApplePowerSourceAdapter1 implements LowPowerSource { private PowerSource adaptee = null; public ApplePowerSourceAdapter1(PowerSource adaptee) { super(); this.adaptee = adaptee; } @Override public int getChargeSupply() { //获得待适配的接口数据 int powerSource220 = adaptee.supplyPower(); //进行一个适配处理 powerSource220 = 36; return powerSource220; } }
这个适配器持有了待适配接口的具体实现,通用性更强,它可以适配任意需要适配的电源。
实验结果如下:
phone.charge(new ApplePowerSourceAdapater1(powerSource220));
apple phone is in charge!
以上这些是我最近对于适配器模式的思考,如果有啥不准确的地方,大家不要介意!!