在软件的开发过程中,我们经常会在已有的系统或类的基础上进行一些扩展。在扩展的过程中,有一些类可能直接为我们所用。还有一些类我们希望使用它们,但是不能直接为我们所用,我们也不能直接修改该类,原因可能是我们没有该类的源代码,无法做出修改;也可能是修改该类付出的代价太大。这就需要我们做出一定的工作来适应对那些类的使用。
在常用模式中,有两种模式涉及到对原有代码的使用:Adapter模式和Visitor模式。本文将谈到Adapter模式,对于Visitor模式,后面也会有一个专题来讲倒它。
记得有一个典故,叫“凿壁借光”。说的是一个读书人,由于家贫,没有钱买蜡烛,以至晚上无法学习。而他的隔壁的那一家人稍微好一些,能买一点蜡烛晚上使用。读书人呢,很想很想使用隔壁家的蜡烛,但他不能去借,因为没有还的;也不能讨,因为人家也不多。怎么办呢?读书人想出了办法:既然我不能把别人的蜡烛拿来,但是我可以想办法改变我家的一些东西,使得我能和他们共用蜡烛啊。于是读书人就通过改变自己家的状态——凿壁,使得蜡烛的光线透过来,而从达到了借光的目的。
在模式中,我们的Adapter模式就是这种“凿壁借光”的模式。我们经常会希望使用这些原有的类,有些可以直接使用;有些由于某种原因,可能是功能的原因等等,我们不能直接使用该类,而需要“凿壁”——我们做一些额外的工作,来使得我们“借光”——能够使用该类。
在什么情况下使用Adapter模式呢?综合起来,有以下三种情况:
<!--[if !supportLists]-->第一, <!--[endif]-->能够直接使用已有的类。
我们假设系统已经存在一个类:WhatIHave类,在这个类里我们有全部或部分的方法需要拿来使用。具体说需要在一个实现了WhatIWant接口的类里使用。
Public class WhatIHave
{
public void f(){……}
public void g(){……}
}
这是我们已经存在的类。
Public interface WhatIWant
{
public void h();
}
这是我们希望实现的类的接口。下面我们来使用Adapter模式来实现WhatIWant接口,同时该实现也拥有WhatIHave类的功能。
Public class WhatIDo
implements WhatIWant
{
private WhatIHave wih = new WhatIHave();
public void f()
{
wih.f();
}
public void g()
{
wih.g();
}
public void h()
{
……
}
}
对WhatIDo类来说,它的情况比“凿壁借光”里的那个读书人境况好一些,它可以把别人的东西直接拿来使用,所以应用Adapter模式的WhatIDo类实现了WhatIWant接口,然后又把WhatIHave类的对象作为它的一个属性,从而达到对WhatIHave类的功能的使用的目的。
上面的一个最最简单的对Adapter模式的使用。可以看出,Adapter模式无非是综合了类的两种再生模式:继承或实现、组合。在上面的那个简单的例子中,Adapter模式使用了组合来达到实用已经存在的类的功能的目的。
<!--[if !supportLists]-->第二, <!--[endif]-->不能够直接使用已有的类。
假设我们系统存在一个Light类,可能就是上面那个读书人希望得到的蜡烛光吧,其代码如下:
public class Light
{
public void turnOn()
{
……
System.out.println(“The light is on,you can do anything……”);
}
public void turnOff()
{
……
System.out.println(“Be careful!the light is power off……”)
}
}
现在读书人通过“凿壁”已经“借到光”了,但是他现在又有了新的需求,就是希望能控制那只蜡烛,“我想让它亮的时候它就亮,想让它灭的时候它就灭”。读书人自己有一个开关,就是Button类。他首先想到的方法是把隔壁家的蜡烛的控制也接到我的开关上,直接来控制那个蜡烛,如下:
public class Button
{
private Light light = new Light();
public void pressOn()
{
light.turnOn();
}
public void pressOff()
{
light.turnOff();
}
}
读书人很高兴,他用了一段时间,但是新的麻烦上来了,他只有这么一个开关,他还想用这个开关做做别的事呢,比如他家的窗户被他设计了一个自动开闭的机关,他也想控制一下那个机关。他就发现不行了,因为Button类只依赖Light类,没有办法加入控制机关的功能。读书人就想,没关系啊,我首先创建一个接口,让Light类和机关都来实现我的接口不就行了。可隔壁家说了,“这是我们的Light类,你不能随意处理”。读书人连忙赔笑道,“您放心,不会的,不会的”。他到底是有学问的人,眼睛一轮,计上心来,暗道,那我就不改变你的东西,改变我的东西,用Adapter模式。
绝大多数的模式都离不开接口,Adapter模式也一样,读书人首先创建了一个接口,如下:
public interface Switch
{
public void turnOn();
public void turnOff();
}
读书人心想,有了这个接口,我的机关就有着落了,它肯定是要实现这个接口的,先不管它,还是想想怎么把Light类的开关“借”过来吧。
接着,读书人创建了一个LightAdapter类,如下:
public class LightAdapter
implements Switch
{
private Light light = new Light();
public void turnOn()
{
light.turnOn();
}
public void turnOff()
{
light.turnOff();
}
}
读书人一击掌,心想,让LightAdapter类去依赖Light类吧,反正我的Button类是不能依赖Light类的,它要依赖Switch接口,这样我的机关只要实现了Switch接口,就能被我的Button类控制了;同时,LightAdapter类也实现了Switch接口,则Light类也可以间接被我的Button类控制了。
以下是他修改后的Button类:
public class Button
{
private Switch switch;
public Button(Switch switch)
{
this.switch = switch;
}
public void pressOn()
{
this.switch.trunOn();
}
public void pressOff()
{
this.switch.turnOff();
}
}
在这里,Adapter模式十分简单:如果客户端要使用某一个已经存在的不能被修改的类,那么让Adapter类(上面的LightAdapter类)去依赖那个已经存在的类;而客户端只依赖Adapter类的接口(上面的Switch类)。
<!--[if !supportLists]-->第三, <!--[endif]-->客户端协议和已经存在类的协议不一致
不久,隔壁家换了一种新型的蜡烛,这种蜡烛能调节光线的亮度,如下:
public class Light
{
private int state;
public Light(int state)
{
this.state = state;
}
public void turnLow()
{
System.out.println(“The state is 1,the light is low……”);
}
public void turnMedium()
{
System.out.println(“The state is 2,the light is
medium……”);
}
public void turnHigh()
{
System.out.println(“The state is 3,the light is high……”);
}
public void turnOff()
{
System.out.println(“The state is 0,the light is off……”);
}
public int getState()
{
return this.state;
}
}
能控制蜡烛的亮度,读书人当然高兴啊。可是他又遇到了麻烦:他的开关只能控制开和关,他又不能将开关改造成多路开关,因为他的很多其他的器件的控制只有开和关,如果开关改了,那么对其他器件的控制势必出现混乱。这时候,简单的使用Button类对Light类进行组合肯定不行了,读书人想,我还是借助于Adapter模式吧。
Public class MultiWayAdapter
implements Switch
{
private Light light;
public MultiWayAdapter(Light light)
{
this.light = light;
}
public void turnOn()
{
int state =
this.light.getState();
switch(state)
case 1 :
this.light.turnLow();
case 2 :
this.light.turnMedium();
case 3 :
this.light.turnHigh();
}
public void turnOff()
{
this.light.turnOff();
}
}
在这个例子里,Adapter类除了实现前面Adapter模式的功能,还担负着另外一个职责:修改已经存在的类的协议,以它满足我们所要求的协议。在Light类里,它有turnLow()、turnMedium()、turnHigh()、turnOff()等功能,而我们的客户端只有turnOn()和turnOff()的功能,我们通过MultiWayAdapter类,将Light类的四种功能转化为Button类所需要的两种功能。
好了,上面我们通过“凿壁借光”的读书人的种种遭遇,来说明了我们为什么要使用Adapter模式,或者说使用Adapter模式的好处。下面我们来谈一谈Adapter模式的几种不同的使用方式。
还是一个简单例子开始。
已经存在的类:
public class WhatIHave {
public void f()
{
System.out.println("It's f() function...");
}
public void g()
{
System.out.println("The g() function......");
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
我们所希望的接口:
public interface WhatIWant {
public void f();
public void g();
public void h();
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
Adapter类:
public class Adapter
implements WhatIWant{
private WhatIHave wih;
public Adapter(WhatIHave wih)
{
this.wih = wih;
}
public void f()
{
this.wih.f();
}
public void g()
{
this.wih.g();
}
public void h()
{
System.out.println("aha,h() function...");
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
使用方法一:
public class WhatIUse {
public void op(WhatIWant wiw)
{
wiw.f();
wiw.g();
wiw.h();
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
这种方法使得客户端依赖接口,而不是具体的实现,具有很强的扩展性。测试代码如下:
WhatIHave wih
= new WhatIHave();
WhatIWant wiw = new
Adapter(wih);
WhatIUse wiu = new
WhatIUse();
wiu.op(wiw);
测试结果如下:
It's
f() function...
The
g() function......
aha,h()
function...
使用方法二:
public class WhatIUse2 {
public void op(WhatIHave wih)
{
WhatIWant wiw = new
Adapter(wih);
wiw.f();
wiw.g();
wiw.h();
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
这种方法依赖于具体类WhatIHave,它的好处是客户端对Adapter类及其接口可以毫无关心。测试代码如下:
WhatIHave wih
= new WhatIHave();
WhatIUse2 wiu2 = new
WhatIUse2();
wiu2.op(wih);
测试结果如下:
It's
f() function...
The
g() function......
aha,h()
function...
使用方法三:
public class Adapter2 extends
WhatIHave implements WhatIWant {
public void h() {
System.out.println("hehe,h() function is comingl...");
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
<!--[if !supportEmptyParas]--> <!--[endif]-->
}
这是改变了Adapter类,让它继承了WhatIHave类,这样的好处当然是Adapter2类实现起来比原来的Adapter类简单得多,但是它同时拥有继承的弱点。测试代码如下:
WhatIWant wiw2
= new Adapter2();
WhatIUse wiu =
new WhatIUse();
wiu.op(wiw2);
测试结果如下:
It's
f() function...
The
g() function......
hehe,h()
function is comingl...
还有一些关于使用Adapter模式的其他方法,在这里就不再阐述了。Adapter模式的关键在于实现Adapter类,它是一个由组合和继承或实现结合起来的类,实现了对已经存在的系统的使用,同时又不修改到已经存在的系统。
|