简体   繁体   English

我能正确理解装饰器模式吗?

[英]Am I understanding the Decorator pattern correctly?

I'm learning the decorator pattern currently. 我正在学习装饰模式。 I wrote this program to test my knowledge. 我写了这个程序来测试我的知识。 Am I getting it right? 我做对了吗?

public interface Logger {
    void log(String msg);
}

public class BasicLogger implements Logger {

    @Override
    public void log(String msg) {
        System.out.println("BasicLogger: " + msg);
    }
}

Here is where I start getting confused, what's the point of the logger.log(msg) in the decorator if I'm going to override it anyway in the HTMLLogger class? 这是我开始感到困惑的地方,如果我要在HTMLLogger类中覆盖它,那么装饰器中的logger.log(msg)有什么HTMLLogger呢?

public class LoggerDecorator implements Logger {
    Logger logger;
    public LoggerDecorator(Logger logger) {
        this.logger = logger;
    }

    @Override
    public void log(String msg) {
        logger.log(msg);
    }
}

Am I even suppoed to duplicate the logger.log(msg); 我甚至支持复制logger.log(msg); line (or whatever the decorator has)? 线(或装饰者有什么)?

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
    }
}

Finally, in the demo class I have this: 最后,在演示类中我有这个:

public LoggerTest() {
    BasicLogger logger = new BasicLogger();
    Logger htmlLogger = new HTMLLogger(new BasicLogger());
    logger.log("Basic Logger log");
    htmlLogger.log("HTML Logging");
}

The output is: 输出是:

BasicLogger: Basic Logger log
BasicLogger: HTML Logging
<HTML> HTML LoggerHTML Logging

I will really need to have a solid understanding of the decorator pattern, because I will need to implement it using AspectJ. 我真的需要对装饰器模式有充分的了解,因为我需要使用AspectJ来实现它。

What you might be missing is a comment saying something like /* your code here */ as instructions for what to do when subclassing. 您可能缺少的是评论,例如/* 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);
    }
}

Each time you call a decorator constructor, you pass in an existing logger. 每次调用装饰器构造函数时,都会传入一个现有的记录器。 This logger is assigned to a private variable and is used in the overridden log method. 此记录器分配给私有变量,并在重写的日志方法中使用。 For each decoration, you add more functionality to the logger.log calls. 对于每个装饰,您都可以为logger.log调用添加更多功能。

For example, when you instantiate your HTMLLogger, you're adding in a system.out message. 例如,当您实例化HTMLLogger时,您将添加system.out消息。 As an exercise, create another concrete logger called XMLLogger, like so: 作为练习,创建另一个名为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
    }
}

and then add it to your test runner: 然后将其添加到您的测试运行器:

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");
}

Output: 输出:

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

In the above output, I inserted a space simply to show you the output resulting from the xmlAndHtmlLogger.log method call. 在上面的输出中,我插入了一个空格,只是为了显示xmlAndHtmlLogger.log方法调用产生的输出。

The decorator pattern works by extending a base class, adding additional code to the overridden method, and then delegating back to the original. 装饰器模式通过扩展基类,向重写方法添加其他代码,然后委托回原始方法来工作。 So for each new decorator subclass you instantiate, you add more functionality to that overridden method. 因此,对于您实例化的每个新装饰器子类,您将为该重写方法添加更多功能。

Since xmlAndHtmlLogger was decorated with the HTMLLogger, which was subclassed from the base, we get all of the functionality of all three when invoking the method. 由于xmlAndHtmlLogger使用HTMLLogger进行了装饰,而HTMLLogger是从基类创建的,因此我们在调用方法时获得了所有这三种功能。 You can mix and match these decorator calls in any order to determine both the order of functionality or to even omit some functionality by leaving out one of the decorators. 您可以按任何顺序混合和匹配这些装饰器调用,以确定功能的顺序,甚至通过省略其中一个装饰器来省略某些功能。


Advantages of Decorator Pattern 装饰模式的优点

I want to clarify that the advantage of the decorator pattern is that you can create different combinations of objects with different mix-and-match functionality, without necessarily creating N concrete subclasses for each combination. 我想澄清一下,装饰器模式的优点是你可以创建具有不同混合和匹配功能的不同对象组合,而不必为每个组合创建N个具体子类。 In this example, with the BaseLogger, HTMLLogger, and XMLLogger, we can create the following objects: 在此示例中,使用BaseLogger,HTMLLogger和XMLLogger,我们可以创建以下对象:

  • A base logger that does nothing fancy. 一个没有任何想象力的基础记录器。
  • An HTML logger HTML记录器
  • An XML logger XML记录器
  • An HTML logger that also handles XML! 一个也处理XML的HTML记录器!

That 4th item is important to note, as it's a combination of two of the decorations. 第四项很重要,因为它是两种装饰的组合。 Let's say we also added the following additional decorators: 假设我们还添加了以下附加装饰器:

  • JSONLogger JSONLogger
  • YAMLLogger YAMLLogger

With these two additional decorators, we now can create the following objects at runtime: 通过这两个额外的装饰器,我们现在可以在运行时创建以下对象:

  • Base logger with nothing fancy. 没有花哨的基础记录器。
  • HTMLLogger HTMLLogger
  • XMLLogger XMLLogger
  • HTML and XML Logger in one HTML和XML Logger合二为一
  • HTML and JSON Logger in one HTML和JSON Logger合二为一
  • HTML and YAML Logger in one HTML和YAML Logger合二为一
  • HTML and XML and JSON Logger in one HTML和XML以及JSON Logger合二为一
  • HTML and XML and JSON and YAML in one HTML和XML以及JSON和YAML合二为一
  • etc 等等

In short, instead of creating a concrete subclass for each desired combination, we simply create simple classes with basic functionality, then we add on extra functionality at runtime by chaining the created objects into the constructor of the next object. 简而言之,我们不是为每个所需的组合创建一个具体的子类,而是简单地创建具有基本功能的简单类,然后通过将创建的对象链接到下一个对象的构造函数中,在运行时添加额外的功能。

Thus, 4 decorators could yield over 16 different combinations of loggers! 因此,4个装饰器可以产生超过16种不同的记录器组合! That's a powerful concept, which can save a lot of coding time. 这是一个强大的概念,可以节省大量的编码时间。

For more in-depth examples, see Wikipedia's WindowScrolling Example , as well as the Coffee Example. 有关更深入的示例,请参阅Wikipedia的WindowScrolling示例以及Coffee示例。 Take note of the constructors in the test runner, where you can clearly see each object passed back into the constructor of the next class. 记下测试运行器中的构造函数,您可以清楚地看到每个对象传递回下一个类的构造函数。 This is the process of "decorating" an object. 这是“装饰”一个物体的过程。

A decorator is simply a wrapper. 装饰器只是一个包装器。 The "wrapping" of an action (method) allows you to "intervene" the action by applying all sort of things before/after it takes place. 动作(方法)的“包装”允许您通过在发生之前/之后应用所有类型的事物来“干预”动作。

That in mind, let's take your first example and modify it to implement the decorator pattern (we can apply different implementations). 记住,让我们拿你的第一个例子并修改它来实现装饰器模式(我们可以应用不同的实现)。

Using inheritance: 使用继承:

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);
    }
} 

Using composition: 使用构图:

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);
    }
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM