1. 装饰器模式定义
装饰器模式(Decorator Pattern)
装饰器模式是一种结构型设计模式,允许向一个对象动态添加行为。在不改变类的接口的情况下,装饰器模式在原始类上增加额外的职责,并且支持多个装饰器嵌套使用。
装饰器模式代码示例
抽象构件类
/**
* 抽象构件类
* 该类声明了具体构件和装饰类中实现的业务方法,使得客户端能以一致的方式处理未装饰和已装饰对象。
*/
public abstract class Component {
// 抽象方法,由具体构件和装饰类实现
public abstract void operation();
}
具体构件类
/**
* 具体构件类
* 该类实现了抽象构件类的业务方法,是被装饰的对象。
*/
public class ConcreteComponent extends Component {
@Override
public void operation() {
System.out.println("ConcreteComponent: 基础功能实现");
}
}
抽象装饰类
/**
* 抽象装饰类
* 该类是装饰者模式的核心,继承了抽象构件类,并持有一个抽象构件类型的对象引用。
*/
public abstract class Decorator extends Component {
// 维持一个对抽象构件对象的引用
protected Component component;
// 注入一个抽象构件类型的对象
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
// 调用原有业务方法
component.operation();
}
}
具体装饰类
/**
* 具体装饰类
* 该类是装饰者模式的具体实现类,向构件添加新的职责。
*/
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation(); // 调用原有业务方法
addedBehavior(); // 调用新增业务方法
}
// 新增业务方法
public void addedBehavior() {
System.out.println("ConcreteDecorator: 新增业务功能");
}
}
客户端使用示例
public class Client {
public static void main(String[] args) {
// 创建具体构件对象
Component component = new ConcreteComponent();
// 使用装饰类进行装饰
Component decoratedComponent = new ConcreteDecorator(component);
// 调用装饰后的方法
decoratedComponent.operation();
}
}
输出结果
ConcreteComponent: 基础功能实现
ConcreteDecorator: 新增业务功能
解析
- 抽象构件类(Component):声明了具体构件和装饰类中实现的业务方法,使得客户端能以一致的方式处理未装饰和已装饰对象。
- 具体构件类(ConcreteComponent):实现了抽象构件类的业务方法,是被装饰的对象。
- 抽象装饰类(Decorator):继承了抽象构件类,持有一个抽象构件类型的对象引用,并实现了业务方法,通过调用引用对象的方法实现对业务方法的调用。
- 具体装饰类(ConcreteDecorator):继承了抽象装饰类,实现了新增的业务方法。
通过这种设计模式,装饰器可以在不修改原始类代码的情况下,为原始类添加新的功能,并且支持多个装饰器的嵌套使用,从而灵活地增强原始类的功能。
2. 装饰器模式与代理模式的区别
代理模式(Proxy Pattern)
代理模式也是一种结构型设计模式,为其他对象提供一个代理,以控制对这个对象的访问。代理模式的主要目的是控制访问,而非增强功能。
-
目的意图不同
- 装饰器模式:主要是为了增强目标类的功能。
- 代理模式:主要是为了控制对目标类的访问。
-
使用差别
- 装饰器模式:装饰器对目标对象没有控制权,目标对象的方法一定会被执行。
- 代理模式:代理对象对目标对象有控制权,可以选择执行或不执行目标对象的方法。
-
对于客户端的关注点
- 装饰器模式:客户端更关心的是对目标对象进行增强后的功能。
- 代理模式:客户端更关心的是被代理对象的功能。
3. 装饰器模式在实际开发中的应用
**需求:**有多个装饰时是怎么保证后面的装饰,在前面装饰的基础上装饰的。比如字符,需要加密+压缩。怎么能让压缩,在加密的基础上压缩?
3.1 创建字符组件接口
public interface StringComponent {
String transform(String str);
}
3.2 实现字符组件
// 字符组件
public class StringEncryptor implements StringComponent {
@Override
public String transform(String str) {
// base64编码
return Base64.getEncoder().encodeToString(str.getBytes());
}
}
3.3 字符加密装饰器
public class StringEncryptorDecorator implements StringComponent {
private StringComponent component;
public StringEncryptorDecorator(StringComponent component) {
this.component = component;
}
@Override
public String transform(String str) {
String encrypted = component.transform(str); // 调用前面一个装饰器或组件的方法
// 这里演示一个简单的压缩方法,将字符串压缩成一行
return encrypted.replaceAll("\\s+", "");
}
}
3.4 字符压缩的装饰器
public class StringCompressorDecorator implements StringComponent {
private StringComponent component;
public StringCompressorDecorator(StringComponent component) {
this.component = component;
}
@Override
public String transform(String str) {
String compressed = component.transform(str); // 调用前面一个装饰器或组件的方法
// 这里演示一个简单的压缩方法,将字符串压缩成一行
return compressed.replaceAll("\\s+", "");
}
}
3.5 客户端
public class Client {
public static void main(String[] args) {
StringComponent component = new StringEncryptor(); // 创建字符加密组件
component = new StringEncryptorDecorator(component); // 用字符加密装饰器装饰它
component = new StringCompressorDecorator(component); // 用字符压缩装饰器再次装饰它
String original = "Hello, world!"; // 原始字符串
String transformed = component.transform(original); // 转换后的字符串
System.out.println(transformed);
}
}
输出结果为:SGVsbG8sIHdvcmxkIQ==
,这是对原始字符串进行加密和压缩后的结果。可以看到,压缩操作在加密操作的基础上进行了。
4. 装饰器模式总结
装饰器模式概述
装饰器模式是一种结构型设计模式,旨在动态地给对象添加额外的职责。
优点:
- 灵活性: 装饰器模式允许在运行时通过添加装饰器来扩展对象的功能,比静态继承更加灵活。
- 可扩展性: 可以对一个对象进行多次装饰,以实现不同的行为或功能。
- 低耦合度: 装饰器模式允许开发者在不修改对象本身的情况下增加新功能。
- 开闭原则: 符合开闭原则,即对扩展开放,对修改封闭。
缺点:
- 对象数量多: 装饰器模式可能会产生许多小的装饰器对象,增加系统的复杂性。
- 错误机会多: 由于装饰器数量可能很多,增加了出错的机会。
- 性能问题: 装饰器模式可能会引入额外的性能开销,尤其是在多层装饰的情况下。
适用场景:
- 动态添加职责: 当需要动态地给对象添加职责,而不是通过继承增加子类。
- 透明性: 需要保持对象的接口不变,即装饰前后对象的接口完全一致。
- 不支持继承: 在不支持或不便于使用继承来扩展功能的场景。