简体   繁体   English

将装饰器设计模式用于类的层次结构

[英]Using the decorator design pattern for a hierarchy of classes

Looking at the following (simplified) hierarchy of classes: 查看以下(简化)类的层次结构:

>     Email (base class) 
>     SimpleEmail extends Email
>     HtmlEmail extends Email

I need to decorate Email.send() to add throttling functionality. 我需要修饰Email.send()以添加限制功能。 I need to instantiate SimpleEmail, HtmlEmail or other similar subclasses of Email. 我需要实例化SimpleEmail,HtmlEmail或其他类似的Email子类。

What should this pattern look like exactly? 这个模式到底应该是什么样的? My guess (which surly needs correcting) is as follows: 我的猜测(需要纠正)如下:

class abstract EmailDecorator
   -> Define a constructor: EmailDecorator(Email component)
   -> Implements all methods of Email and passes values through to component
   -> Adds functionality to send() method
class SimpleEmailDecorator extends EmailDecorator
   -> Define a constructor: SimpleEmailDecorator(SimpleEmail component)
   -> Implement all methods of SimpleEmail and pass through to component
class HtmlEmailDirector extends EmaiDecorator
   -> Same as SimpleEmailDecorator

My brain is not wrapping around how I properly deal with important existing subclasses of the base class that I need to "enhance". 我的大脑并没有围绕我如何正确处理我需要“增强”的基类的重要现有子类。 Most examples simplify it down to a point where the inheritance question becomes muddled. 大多数示例将其简化为继承问题变得混乱的程度。

Here's a simplified example of the decorator pattern . 这是装饰器模式的简化示例。 The class hierarchy is restructured as static inner classes so that the whole example is contained in one compilation unit ( as seen on ideone.com ): 类层次结构被重构为static内部类,因此整个示例包含在一个编译单元中( 如ideone.com上所示 ):

public class AnimalDecorator {

    static abstract class Animal {
        public abstract String makeNoise();
    }   
    static class Dog extends Animal {
        @Override public String makeNoise() { return "woof"; }
    }
    static class Cat extends Animal {
        @Override public String makeNoise() { return "meow"; }
    }

    static class Normal extends Animal {
        protected final Animal delegate;
        Normal(Animal delegate)     { this.delegate = delegate; }
        @Override public String makeNoise() {
            return delegate.makeNoise();
        }
    }
    static class Loud extends Normal {
        Loud(Animal delegate)       { super(delegate); }
        @Override public String makeNoise() {
            return String.format("%S!!!", delegate.makeNoise());
        }       
    }
    static class Stuttering extends Normal {
        Stuttering(Animal delegate) { super(delegate); }
        @Override public String makeNoise() {
            return delegate.makeNoise().replaceFirst(".", "$0-$0-$0-$0");
        }
    }

    public static void keepPokingIt(Animal a) {
        // let's skip the details for now...
        System.out.println(a.makeNoise());
    }
    public static void main(String[] args) {
        keepPokingIt(new Cat());
        // meow

        keepPokingIt(new Stuttering(new Dog()));
        // w-w-w-woof

        keepPokingIt(new Loud(new Cat()));
        // MEOW!!!

        keepPokingIt(new Loud(new Stuttering(new Dog())));
        // W-W-W-WOOF!!!        
    }
}

So here we have a simple Animal hierarchy, with Dog and Cat subclasses. 所以这里我们有一个简单的Animal层次结构,包含DogCat子类。 We also have a Normal decorator -- also an Animal -- that simply delegates all methods to another Animal . 我们还有一个Normal装饰器 - 也是一个Animal - 它只是将所有方法委托给另一个Animal That is, it doesn't really do any effective decoration, but it's ready to be subclassed so that actual decorations can be added. 也就是说,它并没有真正做任何有效的装饰,但它已准备好进行子类化,以便可以添加实际的装饰。

We only have one method here, makeNoise() . 我们这里只有一个方法, makeNoise() We then have two kinds of actual decorations, Loud and Stuttering . 然后我们有两种实际的装饰, LoudStuttering (Consider the case where Animal has many methods; then Normal would be most valuable). (考虑Animal有很多方法的情况;那么Normal会是最有价值的)。

We then have a keepPokingIt(Animal) method, which takes ANY Animal , and would do unmentionable things to it until it makeNoise() . 然后我们有一个keepPokingIt(Animal)方法,该方法接受任何 Animal ,并且在makeNoise()之前makeNoise()它做任何 makeNoise()事情。 In our main function, we then keepPokingIt various kinds of animals, decorated with various personality traits. 在我们的main功能中,我们随后keepPokingIt各种各样的动物,装饰着各种个性特征。 Note that we can even stack one decoration on top of another. 请注意,我们甚至可以将一个装饰堆叠在另一个上面。

The exact implementation details may vary, but this simplified example pretty much captures the essence of the decorator pattern. 确切的实现细节可能会有所不同,但这个简化的示例几乎捕获了装饰器模式的本质。


Another example: ForwardingCollection hierarchy from Guava 另一个例子:来自Guava的ForwardingCollection层次结构

In the above example, keepPokingIt only cares that it's an Animal . 在上面的例子中, keepPokingIt只关心它是一个Animal Sometimes you may want to just poke a Cat and not a Dog , or in other ways differentiate the two types. 有时您可能只想戳Cat而不是Dog ,或者以其他方式区分这两种类型。 In those kinds of scenarios, you'd then provide NormalCat , NormalDog , etc. 在这些场景中,您将提供NormalCatNormalDog等。

If you design your type hierarchy well, this should not be a problem. 如果你很好地设计你的类型层次结构,这应该不是问题。 Remember that you don't have to write decorators for each implementation class , but rather one for each type that you care about. 请记住,您不必为每个实现class编写装饰器,而是为您关心的每种类型编写装饰器。 Ideally, each type should even be an interface rather than a concrete class . 理想情况下,每种类型甚至应该是一个interface而不是一个具体的class

Consider the Java Collections Framework type hierarchy, for example. 例如,考虑Java Collections Framework类型层次结构。 We have: 我们有:

Guava conveniently facilitates decorator pattern implementations on top of this type hierarchy: Guava方便地在这种类型层次结构上促进装饰器模式实现:

Note that there is no ForwardingHashMap<K,V> , or a ForwardingTreeSet<E> . 请注意,没有ForwardingHashMap<K,V>ForwardingTreeSet<E> There's probably no need for those anyway. 反正可能没有必要。

See also 也可以看看

  • Effective Java 2nd Edition, Item 18: Prefer interfaces to abstract classes Effective Java 2nd Edition,Item 18:首选接口到抽象类

Related questions 相关问题

If the sub-classes have additional methods, and you want those to be accessible through the decorators, then you will have to write a separate decorator for each subclass. 如果子类有其他方法,并且您希望通过装饰器访问这些方法,则必须为每个子类编写单独的装饰器。 For you particlular problem, I would recommend another solution. 对于您的特殊问题,我建议另一种解决方案。 Remove the send -method from the Email -class and create a new class Mailer that is responsible for sending emails: Email -class中删除send -method并创建一个负责发送电子邮件的新类Mailer

class Mailer {
    public void send(Email mail) {
        // get required info from mail
        String recipents = mail.getRecipents()
        String subject = mail.getSubject()
        String content = mail.getContent()
        // do stuff with all the information
    }
}

This way you can use different ways of sending emails with all types of emails. 这样,您可以使用不同的方式发送电子邮件与所有类型的电子邮件。

Do you need special methods from SimpleEmail or HtmlEmail ? 您需要SimpleEmail或HtmlEmail的特殊方法吗?

If not then a non abstract EmailDecorator will be enough. 如果没有,那么非抽象的EmailDecorator就足够了。 If Email, SimpleEmail or HtmlEmail implement some interfaces, you should really implement them too. 如果Email,SimpleEmail或HtmlEmail实现了某些接口,那么你也应该真正实现它们。

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

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