1)意图
动态地给一个对象添加一些额外的职责。就增加功能而言,Decorator 模式比生成子类更加灵活。
2)结构
装饰模式的结构如图 7-34 所示。
其中:
-
Component 定义一个对象接口,可以给这些对象动态地添加职责。
-
ConcreteComponent 定义一个对象,可以给这个对象添加一些职责。Decorator 维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致的接口。
-
ConcreteDecorator 向组件添加职责。
3)适用性
Decorator 模式适用于:
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
-
处理那些可以撤销的职责。
-
当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类。
4 应用举例
示例:HTTP响应装饰者
假设我们正在构建一个Web应用程序,希望对某些HTTP响应进行特殊处理,比如添加安全相关的HTTP头,或者对响应内容进行GZIP压缩以提高传输效率。我们可以使用装饰者模式来实现这些功能。
- 定义Component接口
首先,我们需要定义一个接口来表示HTTP响应。
public interface HttpResponse {
void sendResponse(String content);
}
- 创建ConcreteComponent类
然后,我们创建一个实现了HttpResponse
接口的具体类,用于发送未经过任何装饰的原始HTTP响应。
public class SimpleHttpResponse implements HttpResponse {
@Override
public void sendResponse(String content) {
System.out.println("发送响应: " + content);
}
}
- 创建Decorator抽象类
接下来,我们创建一个装饰者类,该类持有一个HttpResponse
类型的引用,这样我们就可以在实际的响应发送之前或之后添加额外的行为。
public abstract class HttpResponseDecorator implements HttpResponse {
protected HttpResponse wrapped;
public HttpResponseDecorator(HttpResponse wrapped) {
this.wrapped = wrapped;
}
@Override
public void sendResponse(String content) {
wrapped.sendResponse(content);
}
}
- 创建ConcreteDecorator类
现在,我们可以创建具体的装饰者类,用来添加特定的功能,如添加安全头或压缩响应。
- 添加安全头
public class SecurityHeaderDecorator extends HttpResponseDecorator {
public SecurityHeaderDecorator(HttpResponse wrapped) {
super(wrapped);
}
@Override
public void sendResponse(String content) {
System.out.println("添加安全头: X-Content-Type-Options=nosniff");
super.sendResponse(content);
}
}
- 压缩响应
public class GzipResponseDecorator extends HttpResponseDecorator {
public GzipResponseDecorator(HttpResponse wrapped) {
super(wrapped);
}
@Override
public void sendResponse(String content) {
String compressedContent = compress(content); // 假设这里有一个压缩方法
System.out.println("压缩响应内容");
super.sendResponse(compressedContent);
}
private String compress(String content) {
// 这里只是一个示例,实际应用中需要实现真正的压缩逻辑
return "GZIPPED_" + content;
}
}
- 使用装饰者
最后,我们可以通过组合多个装饰者来构建一个复杂的响应处理流程。
public class WebApp {
public static void main(String[] args) {
HttpResponse response = new SimpleHttpResponse();
response = new SecurityHeaderDecorator(response);
response = new GzipResponseDecorator(response);
response.sendResponse("Hello, World!");
// 输出:
// 添加安全头: X-Content-Type-Options=nosniff
// 压缩响应内容
// 发送响应: GZIPPED_Hello, World!
}
}
在这个例子中,SecurityHeaderDecorator
和 GzipResponseDecorator
都是装饰者,它们可以单独或组合使用来增强SimpleHttpResponse
的功能。这种设计不仅保持了代码的清晰度和可维护性,还提供了极大的灵活性,使得我们可以根据需要轻松地添加或移除功能。
示例:不同的格式化功能
假设我们要为文本消息添加不同的格式化功能,比如加粗和斜体。我们可以使用装饰者模式来实现这一需求。
- 定义Component接口
public interface TextComponent {
String getText();
}
- 创建ConcreteComponent类
public class SimpleText implements TextComponent {
private String text;
public SimpleText(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
}
- 创建Decorator抽象类
public abstract class TextDecorator implements TextComponent {
protected TextComponent wrapped;
public TextDecorator(TextComponent wrapped) {
this.wrapped = wrapped;
}
@Override
public String getText() {
return wrapped.getText();
}
}
- 创建ConcreteDecorator类
public class BoldDecorator extends TextDecorator {
public BoldDecorator(TextComponent wrapped) {
super(wrapped);
}
@Override
public String getText() {
return "<b>" + super.getText() + "</b>";
}
}
public class ItalicDecorator extends TextDecorator {
public ItalicDecorator(TextComponent wrapped) {
super(wrapped);
}
@Override
public String getText() {
return "<i>" + super.getText() + "</i>";
}
}
- 使用装饰者
public class Main {
public static void main(String[] args) {
TextComponent simpleText = new SimpleText("Hello, World!");
TextComponent boldText = new BoldDecorator(simpleText);
TextComponent italicBoldText = new ItalicDecorator(boldText);
System.out.println(italicBoldText.getText()); // 输出: <i><b>Hello, World!</b></i>
}
}
在这个例子中,SimpleText
是基本的组件,而 BoldDecorator
和 ItalicDecorator
是装饰者,它们可以动态地为文本添加加粗和斜体的格式。通过这种方式,我们可以在不修改原始文本对象的情况下,为其增加新的功能。
装饰者模式的一个主要优点是它提供了比静态继承更大的灵活性。然而,过度使用装饰者可能会导致系统中存在大量小对象,这可能使得代码难以理解和维护。因此,在选择是否使用装饰者模式时,需要权衡其带来的灵活性和可能引入的复杂性。