我目前正在学习装饰模式。我写了这个程序来测试我的知识。我做对了吗?
public interface Logger {
void log(String msg);
}
public class BasicLogger implements Logger {
@Override
public void log(String msg) {
System.out.println("BasicLogger: " + msg);
}
}
这里是我开始感到困惑的地方,如果我要在HTMLLogger
类中覆盖它,那么装饰器中logger.log(msg)的意义是什么?< / p>
public class LoggerDecorator implements Logger {
Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
@Override
public void log(String msg) {
logger.log(msg);
}
}
我是否支持复制logger.log(msg);
行(或装饰者的任何内容)?
public class HTMLLogger extends LoggerDecorator {
public HTMLLogger(Logger logger) {
super(logger);
}
@Override
public void log(String msg) {
logger.log(msg);
System.out.println("<HTML> HTML Logger" + msg);
//Generate the html file
}
}
最后,在演示类中我有这个:
public LoggerTest() {
BasicLogger logger = new BasicLogger();
Logger htmlLogger = new HTMLLogger(new BasicLogger());
logger.log("Basic Logger log");
htmlLogger.log("HTML Logging");
}
输出结果为:
BasicLogger: Basic Logger log
BasicLogger: HTML Logging
<HTML> HTML LoggerHTML Logging
我真的需要对装饰器模式有充分的了解,因为我需要使用AspectJ来实现它。
答案 0 :(得分:6)
您可能缺少的是评论,例如/* your code here */
,作为子类化时的操作说明。
public class LoggerDecorator implements Logger {
Logger logger;
public LoggerDecorator(Logger logger) {
this.logger = logger;
}
@Override
public void log(String msg) {
/** YOUR CODE HERE WHEN SUBCLASSING **/
/**
* delegate to the base logger passed into the constructor to
* perform existing logging operations.
*/
logger.log(msg);
}
}
每次调用装饰器构造函数时,都会传入一个现有的记录器。此记录器分配给私有变量,并在重写的日志方法中使用。对于每个装饰,您都可以为logger.log调用添加更多功能。
例如,当您实例化HTMLLogger时,您将添加system.out消息。作为练习,创建另一个名为XMLLogger的具体记录器,如下所示:
public class XMLLogger extends LoggerDecorator {
public XMLLogger(Logger logger) {
super(logger);
}
@Override
public void log(String msg) {
logger.log(msg);
System.out.println("<?xml version="1.0"?><message>XML Logger" + msg);
//Generate the xml file
}
}
然后将其添加到您的测试运行器中:
public LoggerTest() {
BasicLogger logger = new BasicLogger();
Logger htmlLogger = new HTMLLogger(new BasicLogger());
Logger xmlAndHtmlLogger = new XMLLogger(new HTMLLogger());
logger.log("Basic Logger log");
htmlLogger.log("HTML Logging");
xmlAndHtmlLogger.log("I am Both HTML and XML logging");
}
输出:
BasicLogger: Basic Logger log
BasicLogger: HTML Logging
<HTML> HTML LoggerHTML Logging
BasicLogger: I am Both HTML and XML logging
<HTML> HTML LoggerI am Both HTML and XML logging
<?xml version="1.0"?><message>XML LoggerI am Both HTML and XML logging
在上面的输出中,我插入了一个空格,只是为了显示xmlAndHtmlLogger.log方法调用产生的输出。
装饰器模式通过扩展基类,向重写方法添加其他代码,然后委托回原始方法来工作。因此,对于您实例化的每个新装饰器子类,您可以为该重写方法添加更多功能。
由于xmlAndHtmlLogger使用HTMLLogger进行了装饰,而HTMLLogger是从基类创建的,因此我们在调用方法时获得了所有这三种功能。您可以按任何顺序混合和匹配这些装饰器调用,以确定功能的顺序,或者甚至通过省略其中一个装饰器来省略某些功能。
我想澄清一下,装饰器模式的优点是你可以创建具有不同混合和匹配功能的不同对象组合,而不必为每个组合创建N个具体子类。在此示例中,使用BaseLogger,HTMLLogger和XMLLogger,我们可以创建以下对象:
第4项很重要,因为它是两种装饰的组合。假设我们还添加了以下附加装饰器:
通过这两个额外的装饰器,我们现在可以在运行时创建以下对象:
简而言之,我们不是为每个所需组合创建一个具体的子类,而只是创建具有基本功能的简单类,然后通过将创建的对象链接到下一个对象的构造函数中,在运行时添加额外的功能。
因此,4个装饰器可以产生超过16种不同的记录器组合!这是一个强大的概念,可以节省大量的编码时间。
有关更深入的示例,请参阅Wikipedia's WindowScrolling Example以及咖啡示例。记下测试运行器中的构造函数,您可以清楚地看到每个对象传递回下一个类的构造函数。这是“装饰”一个物体的过程。
答案 1 :(得分:2)
装饰器只是一个包装器。 &#34;包装&#34;动作(方法)允许你干预&#34;通过在发生之前/之后应用所有类型的事物来采取行动。
考虑到这一点,让我们拿你的第一个例子并修改它来实现装饰器模式(我们可以应用不同的实现)。
public class Logger {
void log(String msg){
System.out.println("msg = " + msg);
};
}
public class BasicLogger extends Logger {
@Override
public void log(String msg) {
System.out.println("BasicLogger - before logging: " + msg);
super.log(msg);
System.out.println("BasicLogger - after logging: " + msg);
}
}
public class Logger {
void log(String msg){
System.out.println("msg = " + msg);
};
}
public class BasicLogger {
Logger logger;
public BasicLogger(Logger logger) {
this.logger = logger;
}
public void log(String msg) {
System.out.println("BasicLogger - before logging: " + msg);
logger.log(msg);
System.out.println("BasicLogger - after logging: " + msg);
}
}