适配器模式从实现方式上分为两种,类适配器和对象适配器,这两种的区别在于实现方式上的不同,一种采用继承,一种采用组合的方式。
另外从使用目的上来说,也可以分为两种,特殊适配器和缺省适配器,这两种的区别在于使用目的上的不同,一种为了复用原有的代码并适配当前的接口,一种为了提供缺省的实现,避免子类需要实现不该实现的方法。
类适配器和对象适配器的权衡
● 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。● 对于类适配器,由于适配器直接继承了Adapter,使得适配器不能和Adapter的子类一起工作,因为继承是静态的关系,当适配器继承了Adapter后,就不可能再去处理Adapter的子类了。
● 对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
● 对于类适配器,适配器可以重定义Adapter的部分行为,相当于子类覆盖父类的部分实现方法。
● 对于对象适配器,要重定义Adapter的行为比较困难,这种情况下,需要定义Adapter的子类来实现重定义,然后让适配器组合子类。虽然重定义Adapter的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
● 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adapter。对于对象适配器,需要额外的引用来间接得到Adapter。
建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。
下面举几个例子
类适配器
有一个已经存在的接口
package com.zndroid.dm.AdapterModel.ExtendsAdapter;
/**
* Created by luzhenyu on 2017/7/27.
*/
public interface IStationary {
void doSome();
}
有一个已经存在的具体的实例
package com.zndroid.dm.AdapterModel.ExtendsAdapter;
/**
* Created by luzhenyu on 2017/7/27.
*/
public class Stationary {
public void disPlay() {
System.out.println("stationary display");
}
}
为了既使用该接口又复用原来的实例实现的方式就是类适配器方式
package com.zndroid.dm.AdapterModel.ExtendsAdapter;
/**
* Created by luzhenyu on 2017/7/27.
* 我们需要得到一个接口的类,但是又想用原有的类的功能,但是我们又改不了这个原来的类的代码,
* 或者原来的类有一个完整的类体系,我们不希望破坏它,那么适配器模式就是你的不二之选了
*
* 本案例是类适配器,就是通过继承我们希望复用其功能的类,并且实现我们想适配的接口。(一般是针对适配目标是接口的情况下使用)
* 由于Java单继承的关系所以引入了另外一个适配器 - 对象适配器(采用组合的方式实现)
*/
//在保证不改变Stationary 和 IStationary代码的情况下 重新创建的这个类就是适配器,他同时拥有接口和父类的功能
public class MixtureAdapter extends Stationary implements IStationary {
@Override
public void doSome() {
System.out.println("mixtureAdapter doSome");
}
@Override
public void disPlay() {
super.disPlay();
System.out.println("mixtureAdapter display");
}
}
具体使用:
MixtureAdapter mixtureAdapter = new MixtureAdapter();
mixtureAdapter.doSome();
mixtureAdapter.disPlay();
对象适配器
我有一个已经继承其他对象的实例
package com.zndroid.dm.AdapterModel.ObjectAdapter.impl;
import com.zndroid.dm.AdapterModel.ObjectAdapter.BaseModel;
/**
* Created by luzhenyu on 2017/8/4.
*/
public class User extends BaseModel {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
package com.zndroid.dm.AdapterModel.ObjectAdapter;
/**
* Created by luzhenyu on 2017/8/4.
*/
public class BaseModel {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
我又有了一个已经实现好功能的其他对象
package com.zndroid.dm.AdapterModel.ObjectAdapter;
/**
* Created by luzhenyu on 2017/8/4.
*/
public class OtherClass {
private boolean isDubug = true;
public void otherFun(String msg){ System.out.println("OtherClass - otherFun:" + msg);}
public void setSome(boolean isDubug){
System.out.println("OtherClass - setSome:" + isDubug);
this.isDubug = isDubug;}
}
我为了能够复用这两个已经存在的类,因为不能多继承,所以采用对象适配器模式,即组合已有功能的引用
package com.zndroid.dm.AdapterModel.ObjectAdapter;
import com.zndroid.dm.AdapterModel.ObjectAdapter.impl.User;
/**
* Created by luzhenyu on 2017/8/4.
*
* 针对适配目标是类或者是需要复用的对象多于一个的时候使用,
* 这里再专门提示一下,对象适配器有时候是为了将多个类一起适配,所以才不得不使用组合的方式,而且我们采用对象适配器的时候,继承也不是必须的,
* 而是根据实际的类之间的关系来进行处理,上述例子当中一定要直接或间接的继承自BaseEntity是为了不破坏我们原来的继承体系,但有些情况下这并不是必须的。
*/
public class OtherClassUserAdapter extends User {
private OtherClass otherClass = new OtherClass();//让OtherClassUserAdapter同时拥有User特性和OtherClass特性
public void otherFun(String msg){
otherClass.otherFun(msg);
}
public void setSome(boolean isDubug){
otherClass.setSome(isDubug);
}
}
具体使用:
/**
* (对象适配器)
* 与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,
* 与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adapter类,而是使用委派关系连接到Adapter类。
* */
OtherClassUserAdapter otherClassUserAdapter = new OtherClassUserAdapter();
otherClassUserAdapter.setAge("25");
log(otherClassUserAdapter.getAge());
otherClassUserAdapter.otherFun("OtherClassUserAdapter invoking OtherClass's function");
otherClassUserAdapter.setSome(false);
缺省适配器
对于某个接口有很多行为,然而我们要适配的对象并不是都关系其中的行为,有可能只是使用其中的一或者两个行为,为了适配这种情况我们采用缺省适配器模式,即改缺省的适配器实现了接口的所有默认行为,对于将来要适配的对象只需要挑自己关心的方法进行复写就行了。
我有一个已经存在的接口,里面有好几种行为:
package com.zndroid.dm.AdapterModel.DefaultAdapter;
/**
* Created by luzhenyu on 2017/8/4.
*/
public interface ICommon {
void speck();
void eat();
boolean drink();
void work(String area);
}
但是接下来想复用该接口并扩展的对象并不都是关心接口里面所有的行为,所以采用一个默认的适配器去实现该接口的所有默认行为:
package com.zndroid.dm.AdapterModel.DefaultAdapter;
/**
* Created by luzhenyu on 2017/8/4.
* 接口设计的最小化只是理想状态,难免会有一些实现类,对其中某些方法不感兴趣,
* 这时候,如果方法过多,子类也很多,并且子类的大部分方法都是空着的,那么就可以采取这种方式了。
*
* 当然,这样做违背了里氏替换原则,但是上面的做法原本就违背了接口的最小化原则,所以我们在真正使用时要权衡二者的利弊,
* 到底我们需要的是什么。所以从此也可以看出来,原则只是指导,并不一定也不可能全部满足,所以我们一定要学会取舍。
*/
public class DefaultAdapter implements ICommon {
@Override
public void speck() {
System.out.println("DefaultAdapter: " + "speck");
}
@Override
public void eat() {
System.out.println("DefaultAdapter: " + "eat");
}
@Override
public boolean drink() {
System.out.println("DefaultAdapter: " + "drink");
return false;
}
@Override
public void work(String area) {
System.out.println("DefaultAdapter: " + "work at " + area);
}
}
接下来就是要适配的对象1
package com.zndroid.dm.AdapterModel.DefaultAdapter.impl;
import com.zndroid.dm.AdapterModel.DefaultAdapter.DefaultAdapter;
/**
* Created by luzhenyu on 2017/8/4.
* 去实现我关心的方法,其他不关心
*/
public class DefaultAdapterImpl1 extends DefaultAdapter {
@Override
public void speck() {
System.out.println("DefaultAdapterImpl1: " + "speck");
}
@Override
public void eat() {
System.out.println("DefaultAdapterImpl1: " + "eat");
}
@Override
public boolean drink() {
System.out.println("DefaultAdapterImpl1: " + "drink");
return true;
}
}
要适配的对象2
package com.zndroid.dm.AdapterModel.DefaultAdapter.impl;
import com.zndroid.dm.AdapterModel.DefaultAdapter.DefaultAdapter;
/**
* Created by luzhenyu on 2017/8/4.
*/
public class DefaultAdapterImpl2 extends DefaultAdapter {
@Override
public void eat() {
System.out.println("DefaultAdapterImpl2: " + "eat");
}
@Override
public boolean drink() {
System.out.println("DefaultAdapterImpl2: " + "drink");
return false;
}
@Override
public void work(String area) {
System.out.println("DefaultAdapterImpl2: " + "work at " + area);
}
}
具体使用:
/**
* (缺省适配器)
* 用于接口定义过多,然而实现者不需要关系接口中的某些方法,从中抽取缺省的适配器去实现接口的所有方法,真正实现者继承该
* 缺省适配器去复写自己关心的方法就行了。
* */
DefaultAdapterImpl1 defaultAdapterImpl1 = new DefaultAdapterImpl1();
defaultAdapterImpl1.drink();
defaultAdapterImpl1.eat();
defaultAdapterImpl1.speck();
defaultAdapterImpl1.work("shanghai");
DefaultAdapterImpl2 defaultAdapterImpl2 = new DefaultAdapterImpl2();
defaultAdapterImpl2.drink();
defaultAdapterImpl2.eat();
defaultAdapterImpl2.speck();
defaultAdapterImpl2.work("suzhou");
log("----------------我是分割线-----------------");
【欢迎上码】
【微信公众号搜索 h2o2s2】