也说 Adapter Pattern

米嘉刚发了一篇Extension Object/Interface模式,周五很忙,但还是抽空看完了。不错,把Eclipse里面究竟为什么用Extension Object以及为什么用讲的很清楚。我自己非常喜欢看各种关于设计模式的书和文章,每一次读都会有不同的认识。终于今晚有时间,就也来说说Adapter。

按照GoF的说法,Adapter的意图是将一个类的接口转化为我们需要的其它的接口。这通常发生在这种情况下,比如我们拿到某个软件包甚至源码,但是我们不能修改她的接口,或者修改存在着潜在的风险。

其实,我觉得这不是最关键的问题,关键的问题是我们为什么不能改变自己的接口?为什么不干脆就直接使用软件包的接口算了?

仔细分析一下,其实Adapter算是我们平常用的最多的一种设计模式,可能甚至多到连你自己都没有意识到你是在用Adapter。我来举个例子,也是Adpater运用的一种最典型的情况:多态编程。

Xerdoc DSearch Parser

在XerdocDS中,对硬盘所有文件的扫描都是通过IParser的多态调用来进行解析的,这个接口是我们不能改变的。而事实上,很多具体的Parser,都有现成类可用,只不过接口和我们的不一样,比如上面的HTMLParser。这就需要用Adapter来适配一下。

在这种多态调用的结构下,我们都是针对接口来进行访问的,你不可能更改整个体系的接口,所以,必须要采用Adapter来进行适配。

再深究一下,可以发现,这种Adapter是针对对象的适配,也就是在对象的层面上来扩展功能。如果我希望给扩展接口呢?很简单,多重继承(C++)就派上用场了。

小到这样的类结构设计,大到整个软件架构,Adapter的运用也非常广。我现在在做的一个Web Service,存在两个版本,由于中间一些比较大的变化,新版本已经几乎是一个完全新的Service,从接口到内部算法,都做了很大的改变。这样,新的Service中的接口已经和老版本完全不同,但是,我们还是有很多老的用户在使用老的Web Service接口,这就需要一个Adapter,在内部起一个适配的作用,以便来达到和老版本的兼容性。

可以说,上面举的例子都是Adapter比较经典的用法,本质上说,就是让由于接口的不一致而不能在一起工作的类,在一起工作的一种模式。

Eclipse把这种模式更加发扬广大。Eclipse的一大目标就是要无限扩展。除了插件的部分,扩展现有类的接口也是很重要的一个部分。很显然,我们不能修改现有类的接口,那么,如何给一个现有的类动态的增加新的功能呢?Decorator可以在相同接口下添加其它功能,而Adapter就可以添加其它的接口来添加需要的功能。这样,即可以无限扩充了系统的功能,又保证了系统现有类的纯洁性,也就是单一指责原则。不会造成各种正交功能的对类的污染。

在Eclipse中,所有希望在将来被扩展的类,都需要实现这个接口。

  1. public interface IAdaptable { 
  2.     public Object getAdapter(class adapter);
  3. }

这和上面的需求正好相反,这里不是为了满足现有不匹配或者兼容的需求,而是为了满足未来可能的需求,而使用Adapter。叫Adapter似乎不太合适,叫Extension比较合适,呵呵。好像Eclipse早期的版本里面也确实不叫IAdaptable而叫做IExtensiable吧。

这样带来的问题就是未来的需求会很多,可能会有各种各样的Adapter,如何更好的管理、调用这些Adapter?Eric Gamma的AdapterManager和IAdapterFactory就派上了用场。

IAdapterFactory用来生产某些种类的适配器,而这些Factory和具体需要适配的类的关系由AdapterManager来维护,非常清楚。只需要在系统启动的时候把AdapterFactory都注册到AdapterManager中,然后在需要的时候来进行查询相应的查询即可,具体实现可以看看米嘉的文章。

之所以做出这样的设计,也是和OO设计的一些基本原则密不可分的,比如"针对接口编程"原则,比如"单一职责原则"。其实,这些原则也都是和现实世界密不可分的,比如针对接口编程对应到现实生活中,也就是针对标准进行设计。这样,各家的各种产品才能在一起工作。Adapter的经典例子便是不同制式的电源插销,去香港出差的时候,如果你不带一个大转小的Adapter,肯定无法进行工作:P。

最后举个简单的例子,比如Eclipse的SWT有一些UI元素,Button、List等等,你现在需要做一个Save All功能,就是把这些UI对象的状态序列化到硬盘上,并且能够下次再读取出来。

我可以定义一个接口,叫做:IPersistenable,然后让这些UI元素(Button、List等等)都实现这个接口,然后我就可以这样了:

  1. public interface IPersistensable {
  2.     public void save();
  3. }
  4. public void saveAll() {
  5.     for(IPersistenable p : uiList) {
  6.         p.save();
  7.     }
  8. }

现在的问题是,我无法或者不想改变Button、List等的代码。这时候,就可以采用下面这种方式了:

  1. public void saveAll() {
  2.     for(PlatformObject o : uiList) {
  3.         IPersistensable p = o.getAdapter(IPersistensable.class);
  4.         if(p != null) {
  5.             p.save();
  6.         }
  7.     }
  8. }

也就是说,我们需要实现一个Button->IPersistentable的Adapter,然后将它注册到AdapterManager中,就可以动态的为Button、List等类添加了新的IPersistensable接口。

Popularity: 45%

Related entries:

  • No Related Posts

2 Responses to “也说 Adapter Pattern”

  1. Winters Mi Says:

    我以前的工作基本就是在做Adapter,呵呵

  2. pipilu Says:

    发现您很擅长讲解,讲的有趣味,而且通俗易懂!~

Leave a comment

(required)

(required)


Information for comment users
Line and paragraph breaks are implemented automatically. Your e-mail address is never displayed. Please consider what you're posting.

Use the buttons below to customise your comment.


RSS feed for comments on this post | TrackBack URI

 

Creative Commons License
This work is licensed under a Creative Commons License.

我只有两天,我没有把握,一天用来出生,一天用来死亡