装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern)
装饰器模式是一种结构型设计模式,它允许动态地给一个对象添加一些额外的行为,而无需修改其原始类。装饰器模式通过将对象包装在装饰器类中,来扩展对象的功能。这种方式比继承更加灵活,因为它可以在运行时动态地添加或移除功能。
模式结构
- 组件接口(Component)
定义被装饰对象的接口,声明对象的方法。 - 具体组件(Concrete Component)
实现组件接口,表示被装饰的原始对象。 - 装饰器抽象类(Decorator)
实现组件接口,并持有一个组件对象的引用。它可以是抽象类,用于定义装饰器的基本结构。 - 具体装饰器(Concrete Decorator)
继承装饰器抽象类,并在其中添加额外的行为。
代码示例
1. 组件接口(Component)
// 组件接口:定义被装饰对象的接口
interface DataSource {
void write(String data);
String read();
}
2. 具体组件(Concrete Component)
// 具体组件:文件源
class FileDataSource implements DataSource {
private final String name;
public FileDataSource(String name) {
this.name = name;
}
@Override
public void write(String data) {
File file = new File(name);
try (OutputStream out = new FileOutputStream(file)) {
out.write(data.getBytes(), 0, data.length());
} catch (IOException e) {
System.out.println(e);
}
}
@Override
public String read() {
char[] buf = null;
File file = new File(name);
try (FileReader in = new FileReader(file)) {
buf = new char[(int) file.length()];
in.read(buf);
} catch (IOException e) {
System.out.println(e);
}
return new String(buf);
}
}
3. 装饰器抽象类(Decorator)
// 装饰器抽象类:实现组件接口,并持有一个组件对象的引用
abstract class DataSourceDecorator implements DataSource {
private final DataSource dataSource;
public DataSourceDecorator(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void write(String data) {
dataSource.write(data);
}
@Override
public String read() {
return dataSource.read();
}
}
4. 具体装饰器(Concrete Decorator)
// 具体装饰器:加密装饰器
class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource dataSource) {
super(dataSource);
}
@Override
public void write(String data) {
super.write(encode(data));
}
@Override
public String read() {
return decode(super.read());
}
// 加密
private String encode(String data) {
byte[] res = data.getBytes();
for (int i = 0; i < res.length; i++) {
res[i] += (byte) 1;
}
return Base64.getEncoder().encodeToString(res);
}
// 解密
private String decode(String data) {
byte[] res = Base64.getDecoder().decode(data);
for (int i = 0; i < res.length; i++) {
res[i] -= (byte) 1;
}
return new String(res);
}
}
// 具体装饰器:压缩装饰器
class CompressionDecorator extends DataSourceDecorator {
private int compLevel = 6;
public CompressionDecorator(DataSource dataSource) {
super(dataSource);
}
@Override
public void write(String data) {
super.write(compress(data));
}
@Override
public String read() {
return decompress(super.read());
}
// 压缩
private String compress(String data) {
byte[] src = data.getBytes();
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream(512);
DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));
dos.write(src);
dos.close();
bout.close();
return Base64.getEncoder().encodeToString(bout.toByteArray());
} catch (IOException ex) {
return null;
}
}
// 解压
private String decompress(String data) {
byte[] src = Base64.getDecoder().decode(data);
try {
InputStream in = new ByteArrayInputStream(src);
InflaterInputStream iis = new InflaterInputStream(in);
ByteArrayOutputStream bos = new ByteArrayOutputStream(512);
int len;
while ((len = iis.read()) != -1) {
bos.write(len);
}
bos.close();
iis.close();
in.close();
return new String(bos.toByteArray());
} catch (IOException ex) {
return null;
}
}
}
5.调用示例
public class DecoratorPattern {
public static void main(String[] args) {
String data = "Name,Salary\nJohn Smith,100000\nSteven Jobs,912000";
// 使用多层装饰器
DataSourceDecorator encoded = new CompressionDecorator(new EncryptionDecorator(new FileDataSource("OutputDemo.txt")));
encoded.write(data);
// 读取数据
DataSource dataSource = new FileDataSource("OutputDemo.txt");
System.out.println("- INPUT --------------");
System.out.println(data);
System.out.println("- ENCODED ---------------");
System.out.println(dataSource.read());
System.out.println("- DECODED ---------------");
System.out.println(encoded.read());
}
}
输出结果
- INPUT --------------
Name,Salary
John Smith,100000
Steven Jobs,912000
- ENCODED ---------------
[加密后的数据]
- DECODED ---------------
Name,Salary
John Smith,100000
Steven Jobs,912000
应用场景
- 动态扩展功能
当需要在不修改现有代码的情况下,动态地为对象添加功能时,可以使用装饰器模式。 - 替代继承
当使用继承会导致类爆炸(类数量过多)时,可以使用装饰器模式来替代继承。 - 不可修改的类
当需要扩展一个不可修改的类(如第三方库中的类)时,可以使用装饰器模式。
在Java中的应用
-
JAVA I/O
库InputStream
和OutputStream
是组件接口。FileInputStream
和FileOutputStream
是具体组件。BufferedInputStream
和BufferedOutputStream
是具体装饰器,它们为输入输出流添加了缓冲功能。
优缺点
优点
- 灵活性
可以在运行时动态地添加或移除功能,而不需要修改原始类。 - 避免类爆炸
通过组合而不是继承来扩展功能,避免了类数量过多的问题。 - 单一职责原则
每个装饰器类只负责一个特定的功能,符合单一职责原则。
缺点
- 复杂性增加
使用装饰器模式会增加系统中的类数量,可能导致代码结构复杂。 - 调试困难
由于装饰器是层层包装的,调试时可能需要逐层检查,增加了调试的难度。
总结
装饰器模式通过将对象包装在装饰器类中,提供了一种灵活的方式来扩展对象的功能。它适用于需要动态添加功能的场景,尤其是在不修改现有代码的情况下。尽管它可能会增加系统的复杂性,但其优点在于提高了代码的可维护性和灵活性。在Java中,装饰器模式广泛应用于I/O库中,是理解和使用该模式的经典示例。