适配器模式与装饰模式统称为包装模式。
适配器模式的功能就是把一个类的接口变成客户端能接受的另一种接口,从而使两个接口不匹配而无法在一起工作的两个类能够在一起工作。
适配器的作用就是将一个接口适配到另一个接口,在Java的I/O类库中有很多这样的需求,如将字符串数据转变成字节数据保存到文件中,将字节数据转变成流数据等。
我们知道,Java I/O中InputStreamReader和OutputStreamWriter两个类是字符流和字节流转换的桥梁。InputStreamReader和OutputStreamWriter类分别继承了Reader和Writer接口,但是要创建它们的对象必须在构造函数中传入一个InputStream和OutputStream的实例。InputStreamReader和OutputStreamWriter的作用也就是将InputStream和OutputStream适配到Reader和Writer。InputStreamReader的类结构图如下:
InputStreamReader实现了Reader接口,并且持有了InputStream的引用,这里是通过StreamDecoder类间接持有的,因为从byte到char需要经过编码。
很显然,适配器就是InputStreamReader类,源角色就是InputStream代表的实例对象,目标接口就是Reader类。OutputStreamWriter类也是类似的方式。
在I/O类库中还有很多类似的用法,如StringReader将一个String类适配到Reader接口,ByteArrayInputStream适配器将byte数组适配到InputStream流处理接口。
源角色代码:
public class Person {
private String name;
private String sex;
private int age;
public void speakJapanese(){
System.out.println("I can speak Japanese!");
}
public void speakEnglish(){
System.out.println("I can speak English!");
}
...//以下省略成员变量的get和set方法
}
目标接口代码:
public interface Job {
public abstract void speakJapanese();
public abstract void speakEnglish();
public abstract void speakFrench();
}
适配器的代码:
public class Adapter extends Person implements Job{
public void speakFrench() {
}
}
很显然的,Adapter类继承了Person类,而在Java这种单继承的语言中也就意味着,他不可能再去继承其他的类了,这样也就是这个适配器只为Person这一个类服务。所以称其为类适配模式。
而对象适配器模式是把“源”作为一个对象聚合到适配器类中。
适配器代码:
public class Adapter implements Job {
Person person;
public Adapter(Person person) {
this.person = person;
}
public void speakEnglish() {
person.speakEnglish();
}
public void speakJapanese() {
person.speakJapanese();
}
//new add
public void speakFrench() {
}
}
类的适配模式用于单一源的适配,由于它的源的单一话,代码实现不用写选择逻辑,很清晰;而对象的适配模式则可用于多源的适配,弥补了类适配模式的不足,使得原本用类适配模式需要写很多适配器的情况不复存在,弱点是,由于源的数目可以较多,所以具体的实现条件选择分支比较多,不太清晰。
装饰模式就是将某个类重新包装一下,使它比原来更“漂亮”,或者在功能上更强大,但是原来的这个类的使用者不应该感受到这个被装饰前后有什么不同,装饰模式是继承关系的一个替代方案。
在Java I/O类库中有很多不同的功能组合情况,这些不同的功能组合都是使用装饰模式实现的,下面以FilterInputStream为例介绍装饰模式的使用。
FilterInputStream的类结构图:
InputStream类就是以抽象组件存在的,而FilterInputStream就是具体组件,它实现了抽象组件的所有接口,FilterInputStream类无疑就是装饰器角色,它实现了InputStream类的所有接口,并且持有InputStream的对象实例的引用,BufferedInputStream是具体的装饰器实现者,它给InputStream类附加了功能,这个装饰器类的作用就是使得InputStream读取的数据保证在内存中,而提高读取的性能。
public interface Person {
void doCoding();
}
public class Employee implements Person {
@Override
public void doCoding() {
System.out.println("程序员加班写程序啊,写程序,终于写完了。。。");
}
}
public abstract class Manager implements Person{
//装饰器增加功能
public abstract void doCoding();
}
public class ManagerA extends Manager {
private Person person;//给雇员升职
public ManagerA(Person person) {
super();
this.person = person;
}
@Override
public void doCoding() {
doEarlyWork();
person.doCoding();
}
/**
* 项目经理开始前期准备工作
*/
public void doEarlyWork() {
System.out.println("项目经理A做需求分析");
System.out.println("项目经理A做架构设计");
System.out.println("项目经理A做详细设计");
}
}
public class ManagerB extends Manager {
private Person person;//给雇员升职
public ManagerB(Person person) {
super();
this.person = person;
}
@Override
public void doCoding() {
person.doCoding();
doEndWork();
}
/**
* 项目经理开始项目收尾工作
*/
public void doEndWork() {
System.out.println("项目经理B 在做收尾工作");
}
}
装饰器与适配器模式都有一个别名就是包装模式(Wrapper),它们看似都是起到包装一个类或对象的作用,但是使用它们的目的很不一样。适配器模式的意义是要将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的;而装饰模式不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,但是增强原有对象的功能,或者改变原有对象的处理方法而提升性能,所以这两个模式设计的目的是不同的。