實(shí)例形式的變壓器模式的類圖定義如下。
圖2. 實(shí)例變壓器模式的類圖定義
在圖1可以看出,模式所涉及的成員有:
- 目標(biāo)(Target)。這就是我們所期待得到的接口。目標(biāo)可以是實(shí)的或抽象的類。
- 源(Adaptee)。現(xiàn)有需要適配的接口。
- 變壓器(Adapter)。變壓器類是本模式的核心。變壓器把源接口轉(zhuǎn)換成目標(biāo)接口。 顯然,這一角色必須是實(shí)類。
本模式的示范代碼如下:
package com.javapatterns.adapter; public interface Target { /** * Class Adaptee contains operation sampleOperation1. */ void sampleOperation1(); /** * Class Adaptee doesn't contain operation sampleOperation2. */ void sampleOperation2(); }代碼清單4. Target的源代碼。
package com.javapatterns.adapter; public class Adapter implements Target { public Adapter(Adaptee adaptee){ super(); this.adaptee = adaptee; } public void sampleOperation1(){ adaptee.sampleOperation1(); } public void sampleOperation2(){ // Write your code here } private Adaptee adaptee; }代碼清單5. Adapter的源代碼。
package com.javapatterns.adapter; public class Adaptee { public void sampleOperation1(){} }代碼清單6. Adaptee的源代碼。
實(shí)例形式的變壓器模式的效果
第一、 一個(gè)變壓器可以把多種不同的源適配到同一個(gè)目標(biāo)。換言之,同一個(gè)變壓器可以把源類和它的子類都適配到目標(biāo)接口。
第二、 與類形式的變壓器模式相比,要想置換源類的方法就不容易。如果一定要置換掉源類的一個(gè)或多個(gè)方法,就只好先做一個(gè)源類的子類, 將源類的方法置換掉,然后再把源類的子類當(dāng)作真正的源進(jìn)行適配。
第三、 雖然要想置換源類的方法不容易,但是要想增加一些新的方法則方便得很。 而且新增加的方法同時(shí)適用于所有的源。
利用變壓器模式指方為圓
中國(guó)古代有趙高指鹿為馬的故事。鹿與馬有很多相似之處,沒(méi)見(jiàn)過(guò)的人本就分辨不清,指一指可能沒(méi)什么大不了的。 指方為圓是否太過(guò)?非也。本例就是要指方為圓,需要的只是變壓器模式這個(gè)魔術(shù)手指(Magic Finger)。
變壓器模式在本例子的類圖如下。
圖6. 指方為圓的變壓器模式類圖
package com.javapatterns.adapter.cube2ball; public class Cube { public Cube(double width) { this.width = width; } public double calculateVolume() { return width * width * width; } public double calculateFaceArea() { return width * width; } public double getWidth() { return this.width; } public void setWidth(double width) { this.width = width; } private double width; }代碼清單8. Cube類的源代碼。。
package com.javapatterns.adapter.cube2ball; public interface BallIF { double calculateArea(); double calculateVolume(); double getRadius(); void setRadius(double radius); }代碼清單9. BallIF接口的源代碼。
package com.javapatterns.adapter.cube2ball; public class MagicFinger implements BallIF { public MagicFinger(Cube adaptee) { super(); this.adaptee = adaptee; radius = adaptee.getWidth(); } public double calculateArea() { return PI * 4.0D * ( radius * radius ); } public double calculateVolume() { return PI * 4.0D/3.0D * ( radius * radius * radius ); } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } private double radius = 0; private static final double PI = 3.14D; private Cube adaptee; }代碼清單10. MagicFinger類的源代碼。
如果讀者還記得中學(xué)的數(shù)學(xué)的話,應(yīng)該可以看出,我們的指方為圓系統(tǒng)其實(shí)還是有道理的。它接受一個(gè)正方體, 返還此正方體的內(nèi)切球,也就是能放進(jìn)此正方體的最大的球。
顯然,本例子里,我們使用的是實(shí)例形式的變壓器模式。這樣做的好處是,如果一旦我們決定不僅要支持正方體, 而且要支持四面體等多面體,我們可以使用同一個(gè)MagicFinger類,而不必針對(duì)每一個(gè)多面體都建立一個(gè)MagicFinger類。 這樣也比較符合“魔術(shù)手指”這個(gè)名字。