装饰器模式:Decorator
装饰器模式介绍
装饰器模式在不必改变原类文件和不使用继承的情况下,动态的扩展一个对象的功能。它通过创建一个包装对象,用来包装真实的对象
注意:
不改变原类文件
不使用继承
动态扩展
装饰器模式类图UML:
存在四个角色:
Component:为统一接口,也是装饰类和被装饰类的基本类型
ConcreteComponent:为具体实现类,被装饰类,本身是具有完整功能的类
Decorator:装饰类,在内部维护了一个ConcreteComponent的具体实例对象,他一般是父类,仅仅是一个声明,需要的具体的装饰类是其的子类,而子类再装饰具体的产品类
ConcreteDecorator:具体的装饰器类,每一个装饰器类都具有自身特有的装饰效果
代码示例:
/**
* 统一接口,是被装饰类和装饰类的基本类型
*/
public interface Component {
public void service();
}
/**
* 被装饰的原始类,是Component具体实现类
*/
public class ConcreteComponent implements Component {
@Override
public void service() {
System.out.println("原始类具体实现");
}
}
/**
* 装饰器的父类
*/
public class Decorator implements Component {
//持有Component对象引用
private Component component;
//通过构造函数传入Component对象
public Decorator(Component component) {
this.component = component;
}
@Override
public void service() {
this.component.service();
}
}
/**
* 具体装饰器类
* 继承自Decorator
*/
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void serviceA() {
System.out.println("特有功能serviceA");
}
@Override
public void service() {
System.out.println("serviceA针对该方法添加一层包装");
super.service();
System.out.println("serviceA包装结束");
}
}
/**
* 具体装饰器类
* 继承自Decorator
*/
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
public void serviceB() {
System.out.println("特有功能serviceB");
}
@Override
public void service() {
System.out.println("serviceB针对该方法添加一层包装");
super.service();
System.out.println("serviceB包装结束");
}
}
测试代码
public static void main(String[] args) {
ConcreteConponent concreteConponent = new ConcreteConponent();
concreteConponent.service();
System.out.println("-----------------");
ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(concreteConponent);
concreteDecoratorA.service();//调用原有的方法
concreteDecoratorA.serviceA(); //扩展新的功能
System.out.println("------------------");
ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(concreteConponent);
concreteDecoratorB.service();//调用原有的方法
concreteDecoratorB.serviceB();//扩展新的功能
}
}
执行结果:
通过上述代码可知:分别对装饰器类进行的原方法的装饰和新功能的增加,methodA和methodB都是新功能,这些都是装饰器可以做的,至少包含其中的一种功能需求,否则失去了装饰器的意义
IO流中装饰器的作用
public static void main(String[] args) throws IOException {
String path="E:\\java\\IO\\IOTest\\test4.txt";
/**
* InputStream相当于统一的接口即Component,是装饰器类和被装饰器类的基本类型
* FileInputStream相当于原始实现类,对InputStream中的抽象类进行实现
*/
InputStream inputStream = new FileInputStream(path);
/**
*缓冲流
* BufferedInputStream相当于具体的装饰器类
* 对原有的方法进行装饰,比如read,read(byte[])等,增加了缓冲区功能
*
* class FilterInputStream extends InputStream
* FilterInputStream继承自InputStream ,内部持有InputStream 实例对象引用
*
* BufferedInputStream extends FilterInputStream
* FilterInputStream相当于装饰器类的父类Decorator
*/
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
bufferedInputStream.read(new byte[1024]);
//转换流
/**
* InputStreamReader是具体的装饰器类
* 新的功能是进行字节流和字符流的转换
*/
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
}
InputStream就相当于上述的Component接口,只不过这里是一个抽象类,是被装饰器类和装饰器类基本类型
FileInputStream相当于ConcreteComponent,即待装饰的具体对象,它并不是JAVA的IO结构中的一个装饰器类,因为它无法修饰InputStream
FilterInputStream就是一个Decorator,是装饰器的父类
BufferedInputStream就是一个装饰器类
InputStreamReader也是一个装饰器类
在类图上标注了各个类负责的角色,并且使用背景颜色将InputStream和Reader体系分开,其中左半部分就是InputStream的装饰体系,右半部分就是Reader的装饰体系,并且他们之间的桥梁是InputStreamReader,他们每一个装饰体系都与上面标准的装饰器模式类图极其相似
总之:装饰器模式就是一个可以非常灵活的动态扩展类功能的设计模式,它采用组合的方式取代继承,使得各个功能的扩展更加独立和灵活
适配器模式:Adapter
适配器模式介绍
将一个类的接口转化成客户希望的另外的一个接口,Adapter模式是原本由于接口不兼容不能一起工作的类可以一起使用
适配器模式分为两种:类适配器和对象适配器
使用继承的形式使用类适配器
使用组合的形式使用对象适配器
适配器的类图UML:
角色有三种:
Adaptee:源角色,实际真正提供服务的类,需要进行适配的类
Target:目标角色,客户所期望的接口
Adapter: 适配器角色,将源角色适配成目标角色,在内部持有一个源角色的引用
/**
* 目标角色
* 客户期望的接口
*/
public interface E6V {
public void use6V();
}
/**
* 适配器
* 类适配器,通过继承形式实现
*/
public class ClassAdapter extends E220V implements E6V {
//适配过程
@Override
public void use6V() {
use220V();
}
}
/**
* 适配器
* 对象适配器,使用组合形式
*/
public class ObjectAdapter implements E6V {
//持有源角色对象引用
private E220V e220V;
//通过构造函数来实例化属性
public ObjectAdapter(E220V e220V) {
this.e220V = e220V;
}
@Override
public void use6V() {
this.e220V.use220V();
}
}
测试代码:
public static void main(String[] args) {
//期望是6V
//通过类适配器实现
ClassAdapter classAdapter = new ClassAdapter();
classAdapter.use6V();
//通过对象适配器实现
E220V e220V = new E220V();
ObjectAdapter objectAdapter = new ObjectAdapter(e220V);
objectAdapter.use6V();
}
适配器特点:
1.适配器的对象实现目标接口
2.类适配器需要继承自源角色类
3.对象适配器需要持有一个对象的引用,并通过构造函数传递参数
优点:
1.有更好的复用
2.有更好的扩展性,实现了适配器,可以调用自己开发的功能
缺点:
代码的可读性变差
IO流中适配器的使用
适配器角色就是InputStreamReader
被适配的角色是InputStream类的实例对象
目标接口是Reader类
InputStrteamReader实现了Reader接口,并且持有InputStream的引用,这里是通过StreamDecoder类间接持有的,因为从byte到char要经过编码
/******************InputStreamReader类(适配器类)******************/
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
//持有对被适配对象的引用
public InputStreamReader(InputStream in) {
super(in);
try {
//通过StreamDecoder类间接引用被适配的对象
sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
//…(省略的代码)
}
/******************InputStream类(被适配类)******************/
public abstract class InputStream implements Closeable {
//代码省略
}
使用Demo
File file = new File("hello.txt");
FileInputStream in = new FileInputStream(file);
// 将FileInputStream适配成InputStreamReader,即输入的字节流转换成字符流
InputStreamReader inReader = new InputStreamReader(in);
装饰器和适配器异同点:
同:
装饰器和适配器都可以叫做包装模型,都是对原有的类进行包装成新的类或者对象
异:
适配器:将一个接口转化成另一个接口,通过改变接口来达到重复使用的目的
装饰器:不是要改变被装饰对象的接口,而是保持原有的接口,但是新增原有对象不具有的功能,或者改变原有的对象的处理方式而提升性能