适配器模式是最常用的设计模式之一。我们随处可以见到他的身影。例如Android中的某些监听器接口,还有著名的ListAdapter。
适配器模式,实际上是一个接口转换模式。也就是我们要把一个接口转换为另外一个接口。也许你要问,为什么要这么做呢? 当然,我们也可以不必这么做,直接实现所需要的接口,不就成了吗? 对于一些教学上的例子来看,确实是这样,但是,如果要转换的接口很复杂,而且,我们很可能只是需要另外一个接口的某些实现,就可以完成新接口的大部分功能,这样的 话,使用了Adapter模式,对于客户端来说,即使Adapter内的实现发生了变化,也并不影响他的继续使用。
简而言之,使用适配器的场景就是: 新接口使用老的实现来过度。做法有两种,
第一: 实现新接口(不是真正的实现,只是看起来像),集成老的实现类。
第二: 实现新接口(该有的方法必须有,内容可空),包含老接口。
这两种类图分别如下:
[img]http://dl.iteye.com/upload/attachment/0065/4473/8b5d1e8c-4039-34c7-bdea-2aed7fe46279.png[/img]
[img]http://dl.iteye.com/upload/attachment/0065/4475/8e628c2d-9fe7-3088-815c-c046a365fd9c.png[/img]
这两种方法都可以实现Adapter,我更喜欢第二种。优先组合。继承不仅占用了唯一继承权,而且还继承了很多不必要的东西。
下面我们以代码来看看。这个代码使用插座的故事来说明适配器。
首先,很久以前我们的插座是2口的,标准如此。
为了说明问题,方法只是实例而已。我们的插座有两根线,火线零线
然后我们根据这个标准生产了一种插座。GN插座。
很好。一切都很顺利。突然,着火了。。。。为了更安全,我们决定加入一个接地的线吧。
于是新的标准出来了。地线是必须的。
然后又有了三个口的插座。很多电器都变成三口了。可是,我觉得再去买三口插座,太贵了。或者某一天我突然到了个陌生的环境中,发现只有两口的插座怎么办?
为了解决这个问题。我只好买了个转换器。两口转三口。
这里有个小问题,其实这个例子不太合适。稍后再说。
好了,我们看看使用过程。
好了,来说说这个例子的问题,其实这个例子中的两个接口,都很小,实现起来超级方便。完全可以直接实现吧。好比这个插座,大家都买三口的吧,花不了几个钱。。。
而且,我们这里的适配器,完全就是一个三口插座。哈哈。
还有,这里使用的委托的方式实现的。
实际情况中,适配器并不一定完全实现接口方法(必须写出方法,内容可以为空),而且复杂的可重用的实现使用老的接口类。所以上面的代码,仅仅是一个了解。
适配器模式,实际上是一个接口转换模式。也就是我们要把一个接口转换为另外一个接口。也许你要问,为什么要这么做呢? 当然,我们也可以不必这么做,直接实现所需要的接口,不就成了吗? 对于一些教学上的例子来看,确实是这样,但是,如果要转换的接口很复杂,而且,我们很可能只是需要另外一个接口的某些实现,就可以完成新接口的大部分功能,这样的 话,使用了Adapter模式,对于客户端来说,即使Adapter内的实现发生了变化,也并不影响他的继续使用。
简而言之,使用适配器的场景就是: 新接口使用老的实现来过度。做法有两种,
第一: 实现新接口(不是真正的实现,只是看起来像),集成老的实现类。
第二: 实现新接口(该有的方法必须有,内容可空),包含老接口。
这两种类图分别如下:
[img]http://dl.iteye.com/upload/attachment/0065/4473/8b5d1e8c-4039-34c7-bdea-2aed7fe46279.png[/img]
[img]http://dl.iteye.com/upload/attachment/0065/4475/8e628c2d-9fe7-3088-815c-c046a365fd9c.png[/img]
这两种方法都可以实现Adapter,我更喜欢第二种。优先组合。继承不仅占用了唯一继承权,而且还继承了很多不必要的东西。
下面我们以代码来看看。这个代码使用插座的故事来说明适配器。
首先,很久以前我们的插座是2口的,标准如此。
public interface PowerSocket {
public static final int FL = 0;
public static final int ZL = 1;
public int getFrontLine();
public int getZeroLine();
}
为了说明问题,方法只是实例而已。我们的插座有两根线,火线零线
然后我们根据这个标准生产了一种插座。GN插座。
public class GNPowerSocket implements PowerSocket{
@Override
public int getFrontLine() {
// TODO Auto-generated method stub
return PowerSocket.FL;
}
@Override
public int getZeroLine() {
// TODO Auto-generated method stub
return PowerSocket.ZL;
}
}
很好。一切都很顺利。突然,着火了。。。。为了更安全,我们决定加入一个接地的线吧。
于是新的标准出来了。地线是必须的。
public interface NewPowerSocket {
public static final int GL = 3;
public int getFrontLine();
public int getZeroLine();
public int getGroundLine();
}
然后又有了三个口的插座。很多电器都变成三口了。可是,我觉得再去买三口插座,太贵了。或者某一天我突然到了个陌生的环境中,发现只有两口的插座怎么办?
为了解决这个问题。我只好买了个转换器。两口转三口。
public class SocketAdapter implements NewPowerSocket{
private PowerSocket GNps;
public SocketAdapter(PowerSocket ps){
this.GNps = ps;
}
@Override
public int getGroundLine() {
// TODO Auto-generated method stub
return NewPowerSocket.GL;
}
@Override
public int getFrontLine() {
// TODO Auto-generated method stub
return GNps.getFrontLine();
}
@Override
public int getZeroLine() {
// TODO Auto-generated method stub
return GNps.getZeroLine();
}
}
这里有个小问题,其实这个例子不太合适。稍后再说。
好了,我们看看使用过程。
public class Client {
public void useSocket(NewPowerSocket nps){
nps.getFrontLine();
//dosomething...
//...
}
public static void main(String[] args){
Client client = new Client();
PowerSocket ps = new GNPowerSocket();
SocketAdapter adapter = new SocketAdapter(ps);
client.useSocket(adapter);
}
}
好了,来说说这个例子的问题,其实这个例子中的两个接口,都很小,实现起来超级方便。完全可以直接实现吧。好比这个插座,大家都买三口的吧,花不了几个钱。。。
而且,我们这里的适配器,完全就是一个三口插座。哈哈。
还有,这里使用的委托的方式实现的。
实际情况中,适配器并不一定完全实现接口方法(必须写出方法,内容可以为空),而且复杂的可重用的实现使用老的接口类。所以上面的代码,仅仅是一个了解。