最近一直在重溫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!
以上這些是我最近對於適配器模式的思考,如果有啥不准確的地方,大家不要介意!!