实现方式
适配器模式从实现方式上分为两种,类适配器和对象适配器,
这两种的区别在于实现方式上的不同,一种采用继承,一种采用组合的方式。另外从使用目的上来说,也可以分为两种,特殊适配器和缺省适配器,这两种的区别在于使用目的上的不同,
一种为了复用原有的代码并适配当前的接口,一种为了提供缺省的实现,避免子类需要实现不该实现的方法。
适配器模式是补救措施,所以在系统设计过程中请忘掉这个设计模式,这个模式只是在你无可奈何时的补救方式。
使用场景
场景通常情况下是,系统中有一套完整的类结构,而我们需要利用其中某一个类的功能(通俗点说可以说是方法),但是我们的客户端只认识另外一个和这个类结构不相关的接口,这时候就是适配器模式发挥的时候了,我们可以将这个现有的类与我们的目标接口进行适配,最终获得一个符合需要的接口并且包含待复用的类的功能的类。
类适配器
比如说观察者模式的一个缺点,即如果一个现有的类没有实现Observer接口,那么我们就无法将这个类作为观察者加入到被观察者的观察者列表中
在这个问题中,我们需要得到一个Observer接口的类,但是又想用原有的类的功能,但是我们又改不了这个原来的类的代码,或者原来的类有一个完整的类体系,我们不希望破坏它,那么适配器模式就是你的不二之选了。
我们举个具体的例子,比如我们希望将HashMap这个类加到观察者列表里,在被观察者产生变化时,假设我们要清空整个MAP。
因为Observable的观察者列表只认识Observer这个接口,它不认识HashMap,怎么办呢?
这种情况下,我们就可以使用类适配器的方式将我们的HashMap做点手脚,刚才已经说了,类适配器采用继承的方式,那么我们写出如下适配器。
public class HashMapObserverAdapter<K, V> extends HashMap<K, V> implements Observer{
public void update(Observable o, Object arg) {
//被观察者变化时,清空Map
super.clear();
}
}
即我们继承我们希望复用其功能的类,并且实现我们想适配的接口,在这里就是Observer,
那么就会产生一个适配器,这个适配器具有原有类(即HashMap)的功能,又具有观察者接口,所以这个适配器现在可以加入到观察者列表了。
对象适配器
对象适配器是采用组合的方式实现
比如我们现在有一个写好的类,假设就是个实体类如下
public class User extends BaseEntity {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
同样想实现被观察者的功能,还要继承Observable类, 因为java单继承原因,肯定不行
这里使用适配器模式就可以帮我们复用Observable的代码或者说功能
//我们扩展BaseEntity,适配出来一个可观察的实体基类
public class BaseObservableEntity extends BaseEntity{
private Observable observable = new Observable();
public synchronized void addObserver(Observer o) {
observable.addObserver(o);
}
public synchronized void deleteObserver(Observer o) {
observable.deleteObserver(o);
}
public void notifyObservers() {
observable.notifyObservers();
}
public void notifyObservers(Object arg) {
observable.notifyObservers(arg);
}
public synchronized void deleteObservers() {
observable.deleteObservers();
}
protected synchronized void setChanged() {
observable.setChanged();
}
protected synchronized void clearChanged() {
observable.clearChanged();
}
public synchronized boolean hasChanged() {
return observable.hasChanged();
}
public synchronized int countObservers() {
return observable.countObservers();
}
}
以上两种方式都是为了复用现有的代码而采用的适配器模式,根据目的的不同,适配器模式也可以分为两种,那么上述便是第一种,可称为定制适配器,
还有另外一种称为缺省适配器。
缺省适配器
最小接口原则这个原则所表达的思想是说接口的行为应该尽量的少,如果没有做到,结果就是实现这个接口的子类,很可能出现很多方法是空着的情况,因为你的接口设计的过大,导致接口中原本不该出现的方法出现了,结果现在子类根本用不上这个方法,但由于JAVA语言规则的原因,实现一个接口必须实现它的全部方法,所以我们的子类不得不被迫写一堆空方法在那,只为了编译通过。
所以为了解决这一问题,缺省适配器就出现了。比如我们有如下接口。
public interface Person {
void speak();
void listen();
void work();
}
不是所有人都有工作, 可以写如下代码:
public class DefaultPerson implements Person{
public void speak() {
}
public void listen() {
}
public void work() {
}
}
我们创造一个Person接口的默认实现,它里面都是一些默认的方法,当然这里因为没什么可写的就空着了,实际当中可能会加入一些默认情况下的操作,比如如果方法返回结果整数,那么我们在缺省适配器中可以默认返回个0。
只要继承这个默认的适配器(DefaultPerson),然后覆盖掉感兴趣的方法就行了,比如speak和listen,至于work,由于适配器帮我们提供了默认的实现,所以就不需要再写了。
总结
1,第一种类适配器,一般是针对适配目标是接口的情况下使用。
2,第二种对象适配器,一般是针对适配目标是类或者是需要复用的对象多于一个的时候使用,这里再专门提示一下,对象适配器有时候是为了将多个类一起适配,所以才不得不使用组合的方式,而且我们采用对象适配器的时候,继承也不是必须的,而是根据实际的类之间的关系来进行处理,上述例子当中一定要直接或间接的继承自BaseEntity是为了不破坏我们原来的继承体系,但有些情况下这并不是必须的。
对于第三个缺省适配器,一般是为了弥补接口过大所犯下的过错,但是也请注意衡量利弊,权衡好以后再考虑是否要使用缺省适配器。