装饰器模式是一种结构型设计模式,它允许在不修改原有对象结构的情况下,动态地给对象添加新的功能。装饰器模式通过创建一个包装对象(装饰器)来包裹真实的对象,从而在运行时扩展对象的功能。
装饰器模式包含以下角色:
- 组件(Component):定义一个对象接口,可以给这些对象动态地添加职责
- 具体组件(Concrete Component):定义一个对象,可以给这个对象添加一些职责
- 装饰器(Decorator):维持一个指向Component对象的引用,并定义一个与Component接口一致的接口
- 具体装饰器(Concrete Decorator):向组件添加职责
装饰器模式的优缺点
优点:
- 灵活性高:比继承更加灵活,可以在运行时动态地添加或删除功能
- 符合开闭原则:添加新功能时无需修改现有代码
- 职责分离:每个装饰器只关注自己的功能,符合单一职责原则
- 可组合性:可以使用多个装饰器来组合出复杂的功能
- 避免类爆炸:避免了通过继承产生大量子类的问题
缺点:
- 设计复杂:会产生很多小对象,增加系统复杂度
- 调试困难:多层装饰器嵌套时,调试和排查问题比较困难
- 性能影响:多层装饰可能会对性能产生一定影响
什么场景下使用装饰器模式
- 需要动态地给一个对象添加功能,这些功能也可以动态地被撤销
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时
- 需要为一组基本功能动态地添加不同的扩展功能
- 系统中存在大量独立的扩展,每种组合都可能被使用
代码举例
实际开发当中有很多地方用到装饰器模式,最典型的就是Java I/O流的InputStream、OutputStream及其各种装饰器类(BufferedInputStream、DataInputStream等),还有像设计一个多级缓存也可以用到,这里简单举例一个文本处理的例子
// 组件接口 - 文本处理器
interface TextProcessor {String process(String text);
}// 具体组件 - 基础文本处理器
class BasicTextProcessor implements TextProcessor {@Overridepublic String process(String text) {return text;}
}// 装饰器抽象类
abstract class TextProcessorDecorator implements TextProcessor {protected TextProcessor processor;public TextProcessorDecorator(TextProcessor processor) {this.processor = processor;}
}// 具体装饰器 - HTML转义装饰器
class HtmlEscapeDecorator extends TextProcessorDecorator {public HtmlEscapeDecorator(TextProcessor processor) {super(processor);}@Overridepublic String process(String text) {String processed = processor.process(text);return escapeHtml(processed);}private String escapeHtml(String text) {return text.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """).replace("'", "'");}
}// 具体装饰器 - Markdown转换装饰器
class MarkdownDecorator extends TextProcessorDecorator {public MarkdownDecorator(TextProcessor processor) {super(processor);}@Overridepublic String process(String text) {String processed = processor.process(text);return convertMarkdown(processed);}private String convertMarkdown(String text) {// 简化的Markdown转换return text.replace("**", "<strong>").replace("*", "<em>").replace("`", "<code>").replaceAll("^# (.*)$", "<h1>$1</h1>").replaceAll("^## (.*)$", "<h2>$1</h2>");}
}// 具体装饰器 - 敏感词过滤装饰器
class SensitiveWordFilterDecorator extends TextProcessorDecorator {private Set<String> sensitiveWords;public SensitiveWordFilterDecorator(TextProcessor processor) {super(processor);this.sensitiveWords = new HashSet<>(Arrays.asList("暴力", "色情", "赌博", "毒品"));}@Overridepublic String process(String text) {String processed = processor.process(text);return filterSensitiveWords(processed);}private String filterSensitiveWords(String text) {String result = text;for (String word : sensitiveWords) {result = result.replace(word, "*".repeat(word.length()));}return result;}
}// 具体装饰器 - 格式化装饰器
class FormattingDecorator extends TextProcessorDecorator {public FormattingDecorator(TextProcessor processor) {super(processor);}@Overridepublic String process(String text) {String processed = processor.process(text);return formatText(processed);}private String formatText(String text) {// 简单的文本格式化return text.trim().replaceAll("\\s+", " ").replaceAll("\n+", "\n");}
}// 客户端使用示例
public class TextProcessorDemo {public static void main(String[] args) {String inputText = " # 标题\n\n这是一个**重要**的*消息*,包含`代码`。\n\n请注意,这里有一些敏感词汇:暴力和色情内容。 ";// 基础处理器TextProcessor basicProcessor = new BasicTextProcessor();// 添加格式化功能TextProcessor formattingProcessor = new FormattingDecorator(basicProcessor);// 添加Markdown转换功能TextProcessor markdownProcessor = new MarkdownDecorator(formattingProcessor);// 添加HTML转义功能TextProcessor htmlEscapeProcessor = new HtmlEscapeDecorator(markdownProcessor);// 添加敏感词过滤功能TextProcessor fullProcessor = new SensitiveWordFilterDecorator(htmlEscapeProcessor);System.out.println("=== 原始文本 ===");System.out.println(inputText);System.out.println("\n=== 处理后文本 ===");String result = fullProcessor.process(inputText);System.out.println(result);// 只使用部分装饰器System.out.println("\n=== 只使用Markdown和HTML转义 ===");TextProcessor simpleProcessor = new HtmlEscapeDecorator(new MarkdownDecorator(basicProcessor));String simpleResult = simpleProcessor.process("# 标题 <script>");System.out.println(simpleResult);}
}