装饰者模式
定义:装饰是一种结构设计模式, 允许你通过将对象放入特殊封装对象中来为原对象增加新的行为。
为对象增加新的行为可以理解为在原基础上的扩展,抛开设计模式而言,我们首先想到的解决方式是继承,如果你想给一款低配版版汽车装上倒车影像,你可以你的Car可以继承自RoadRadar ,当你想继续提升行使品质的时候,你希望在汽车上安装Bose音响,此时,显然Car不能再继承一个Bose类,因为Java不支持多继承,显然这样也不符合“对修改关闭,对扩展开放”的原则。
引入装饰着模式的思想,我们把Car作为一个被装饰者,RoadRadar、Bose 可以作为对Car的扩展,装饰者模式可以在不修改被装饰者的情况下,增加被修饰者的功能,比生成子类更加灵活。
PrefectCar prefectCar = new RoadRadarAndBoseCar(new RoadRadarCar(new Car));
可见,最初的被装饰者Car对象是始终是被包裹在最内层的,如果接下来再对Car进行包装,我们直接将prefectCar对象作为入参即可,这样便完成了在扩展功能的需求下,做到了不修改被装饰者的属性,符合“对修改关闭,对扩展开放”的原则。
被装饰者要与装饰者继承自同一超类
装饰者模式最显著的特征:可通过以当前类或对象为参数的创建方法或构造函数来识别。
引用:
装饰者模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
在装饰者模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象装饰类,而将具体的装饰类作为它的子类
角色
Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的核心在于抽象装饰类的设计。
作者:小旋锋
链接:https://juejin.cn/post/6844903681322647566
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
挪用refactoringguru上的栗子:文件读取写入流
talk is cheaper,show me your code.
DataSource.java: 定义了读取和写入操作的通用数据接口(本类只作为代码封装用)
package refactoring_guru.decorator.example.decorators;
public interface DataSource {
void writeData(String data);
String readData();
}
FileDataSource.java
/**
* @author Liutx
* @date 2020/12/27 13:40
* @Description 接口实现类,将被装饰者的文件操作代码进行封装实现,是被装饰者的一部分
*/
public class FileDataSource implements DataSource {
private String name;
public FileDataSource(String name) {
this.name = name;
}
@Override
public void writeData(String data) {
File file = new File(name);
try (OutputStream fos = new FileOutputStream(file)) {
fos.write(data.getBytes(), 0, data.length());
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
@Override
public String readData() {
char[] buffer = null;
File file = new File(name);
try (FileReader reader = new FileReader(file)) {
buffer = new char[(int) file.length()];
reader.read(buffer);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
return new String(buffer);
}
}
DataSourceDecorator.java
/**
* @author Liutx
* @date 2020/12/27 13:48
* @Description 抽象基础装饰(被装饰者)
*/
public class DataSourceDecorator implements DataSource{
private DataSource wrappee;
DataSourceDecorator(DataSource source) {
this.wrappee = source;
}
@Override
public void writeData(String data) {
wrappee.writeData(data);
}
@Override
public String readData() {
return wrappee.readData();
}
}
CompressionDecorator.java
/**
* @author Liutx
* @date 2020/12/27 14:09
* @Description 压缩装饰者
*/
public class CompressionDecorator extends DataSourceDecorator {
private int compLevel = 6;
public CompressionDecorator(DataSource source) {
super(source);
}
public int getCompressionLevel() {
return compLevel;
}
public void setCompressionLevel(int value) {
compLevel = value;
}
@Override
public void writeData(String data) {
super.writeData(compress(data));
}
@Override
public String readData() {
return decompress(super.readData());
}
private String compress(String stringData) {
byte[] data = stringData.getBytes();
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));
dos.write(data);
dos.close();
bout.close();
return Base64.getEncoder().encodeToString(bout.toByteArray());
} catch (IOException ex) {
return null;
}
}
private String decompress(String stringData) {
byte[] data = Base64.getDecoder().decode(stringData);
try {
InputStream in = new ByteArrayInputStream(data);
InflaterInputStream iin = new InflaterInputStream(in);
ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
int b;
while ((b = iin.read()) != -1) {
bout.write(b);
}
in.close();
iin.close();
bout.close();
return new String(bout.toByteArray());
} catch (IOException ex) {
return null;
}
}
}
EncryptionDecorator.java
/**
* @author Liutx
* @date 2020/12/27 13:53
* @Description 加解密装饰者
*/
public class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) {
super(source);
}
@Override
public void writeData(String data) {
super.writeData(encode(data));
}
@Override
public String readData() {
return decode(super.readData());
}
private String encode(String data) {
byte[] result = data.getBytes();
for (int i = 0; i < result.length; i++) {
result[i] += (byte) 1;
}
return Base64.getEncoder().encodeToString(result);
}
private String decode(String data) {
byte[] result = Base64.getDecoder().decode(data);
for (int i = 0; i < result.length; i++) {
result[i] -= (byte) 1;
}
return new String(result);
}
}
Demo.java
public class Demo {
public static void main(String[] args) {
String salaryRecords = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000";
DataSourceDecorator encoded = new CompressionDecorator(new DataSourceDecorator(new FileDataSource("out/OutputDemo.txt")));
encoded.writeData(salaryRecords);
//DataSource不要导错包,不是连接池的DataSource
DataSource plain = new FileDataSource("out/OutputDemo.txt");
System.out.println("- Input ----------------");
System.out.println(salaryRecords);
System.out.println("- Encoded --------------");
System.out.println(plain.readData());
System.out.println("- Decoded --------------");
System.out.println(encoded.readData());
}
}
Console输出:
- Input ----------------
Name,Salary
John Smith,100000
Steven Jobs,912000
- Encoded --------------
eJzzS8xN1QlOzEksquTyys/IUwjOzSzJ0DE0AAGu4JLUstQ8Ba/8pGIdS0MjoBAAfpEOqg==
- Decoded --------------
Name,Salary
John Smith,100000
Steven Jobs,912000
装饰者模式及典型应用
- Java I/O 中的装饰者模式
- spring session 中的装饰者模式
- Mybatis 缓存中的装饰者模式