简介
在面向对象编程中,装饰器设计模式提供了一种动态添加功能的方法,而无需改变对象本身的结构。这种模式通过创建一个包含原有对象的封装对象来扩展其行为,从而避免了多重继承的复杂性。
核心组件
- 组件接口(Component):定义了对象的接口,可以给这些对象动态地添加职责;
- 具体组件(Concrete Component):定义了将要接收附加责任的对象;
- 装饰器(Decorator):持有一个组件对象的引用,并定义一个与组件接口一致的接口;
- 具体装饰器(Concrete Decorator):负责给组件添加新的责任;
工作原理
装饰器模式通过创建一个装饰器类,它包含一个组件对象的引用,并在装饰器类中实现组件接口。具体装饰器类通过扩展装饰器类来为组件对象添加新的行为。
使用场景
装饰器模式适用于以下场景:
- 需要扩展功能:当需要为对象动态添加额外的功能时;
- 替代继承:作为一种替代多重继承的方式,以避免继承层次的复杂性;
- 运行时添加功能:在运行时根据需要添加功能,而不是在编译时确定。
构建一个灵活的日志记录系统
创建一个基本的日志记录器,并允许通过装饰器动态地添加额外的日志记录功能,如日志级别过滤,日志格式化和日志持久化。
基本组件
// 日志记录器接口
public interface Logger {
void log(String message);
}
// 基本日志记录器实现
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("LOG: " + message);
}
}
装饰器抽象类
创建一个装饰器抽象类,它持有一个 Logger
对象的引用,并实现 Logger
接口。
// 日志记录器装饰器抽象类
public abstract class LoggerDecorator implements Logger {
protected final Logger decoratedLogger;
public LoggerDecorator(Logger logger) {
this.decoratedLogger = logger;
}
@Override
public void log(String message) {
decoratedLogger.log(message);
}
}
具体装饰器
现在,我们可以创建具体的装饰器来添加不同的功能。
// 日志级别装饰器
public class LogLevelDecorator extends LoggerDecorator {
private final LogLevel level;
public LogLevelDecorator(Logger logger, LogLevel level) {
super(logger);
this.level = level;
}
@Override
public void log(String message) {
if (message.startsWith(level.getPrefix())) {
decoratedLogger.log(message);
}
}
public enum LogLevel {
INFO("INFO"), DEBUG("DEBUG"), ERROR("ERROR");
private final String prefix;
LogLevel(String prefix) {
this.prefix = prefix;
}
public String getPrefix() {
return prefix;
}
}
}
// 日志格式化装饰器
public class LogFormatterDecorator extends LoggerDecorator {
public LogFormatterDecorator(Logger logger) {
super(logger);
}
@Override
public void log(String message) {
String formattedMessage = String.format("%s - %s", new Date(), message);
decoratedLogger.log(formattedMessage);
}
}
// 日志持久化装饰器
public class LogPersistenceDecorator extends LoggerDecorator {
private final String filePath;
public LogPersistenceDecorator(Logger logger, String filePath) {
super(logger);
this.filePath = filePath;
}
@Override
public void log(String message) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath, true))) {
writer.write(message + System.lineSeparator());
} catch (IOException e) {
e.printStackTrace();
}
decoratedLogger.log(message);
}
}
使用装饰器
public class LoggingSystem {
public static void main(String[] args) {
Logger logger = new ConsoleLogger();
logger = new LogFormatterDecorator(logger);
logger = new LogPersistenceDecorator(logger, "logs.txt");
logger = new LogLevelDecorator(logger, LogLevel.INFO);
logger.log("INFO: This is an informational message.");
logger.log("DEBUG: This is a debug message.");
logger.log("ERROR: This is an error message.");
}
}
在这个示例中,我们构建了一个灵活的日志记录系统,它允许在运行时动态地添加日志级别过滤、格式化和持久化。这种灵活性是通过装饰器模式实现的,它允许我们根据需要组合不同的日志记录功能,而无需修改现有的日志记录代码。
优缺点:
优点
- 灵活性:可以灵活地为对象添加功能;
- 扩展性:易于扩展新功能;
- 透明性:装饰器和组件具有一致的接口。
缺点
- 复杂性:过度使用装饰器模式可能会使系统复杂化。
- 性能影响:每次装饰都可能影响性能。
装饰器模式VS代理模式
在代理模式中,代理类附加的是跟原始类无关的功能;
装饰器模式中,装饰器类附加的是跟原始类相关的增强功能。
// 代理模式的代码结构
public interface IA {
void f();
}
public class A impelements IA {
public void f() { //... }
}
public class AProxy implements IA {
private IA a;
public AProxy(IA a) {
this.a = a;
}
public void f() {
// 新添加的代理逻辑
a.f();
// 新添加的代理逻辑
}
}
// 装饰器模式的代码结构
public interface IA {
void f();
}
public class A implements IA {
public void f() { //... }
}
public class ADecorator implements IA {
private IA a;
public ADecorator(IA a) {
this.a = a;
}
public void f() {
// 功能增强代码
a.f();
// 功能增强代码
}
}
结论
装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。