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

查看以下(简化)类的层次结构:

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

我需要修饰Email.send()来添加限制function。 我需要实例化SimpleEmail,HtmlEmail或其他类似的Email子类。

这个模式到底应该是什么样的? 我的猜测(需要纠正)如下:

 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 

我的大脑并没有围绕我如何正确处理我需要“增强”的基类的重要现有子类。 大多数示例将其简化为inheritance问题变得混乱的程度。

这是装饰器模式的简化示例。 类层次结构被重构为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())); // www-woof keepPokingIt(new Loud(new Cat())); // MEOW!!! keepPokingIt(new Loud(new Stuttering(new Dog()))); // WWW-WOOF!!! } } 

所以这里我们有一个简单的Animal层次结构,带有DogCat子类。 我们还有一个Normal装饰器 – 也是一个Animal – 它只是将所有方法委托给另一个Animal 。 也就是说,它并没有真正做任何有效的装饰,但它已准备好进行子类化,以便可以添加实际的装饰。

我们这里只有一个方法, makeNoise() 。 然后我们有两种实际装饰, LoudStuttering 。 (考虑Animal有很多方法的情况;那么Normal会是最有价值的)。

然后我们有一个keepPokingIt(Animal)方法,该方法接受任何 Animal ,并且在makeNoise()之前makeNoise()它做任何 makeNoise()事情。 在我们的mainfunction中,我们随后keepPokingIt各种各样的动物,装饰着各种人格特质。 请注意,我们甚至可以将一个装饰堆叠在另一个上面。

确切的实现细节可能会有所不同,但这个简化的示例几乎捕获了装饰器模式的本质。


另一个例子:来自Guava的ForwardingCollection层次结构

在上面的例子中, keepPokingIt只关心它是一个Animal 。 有时您可能只想戳Cat而不是Dog ,或者以其他方式区分这两种类型。 在这种情况下,您可以提供NormalCatNormalDog等。

如果你很好地设计你的类型层次结构,这应该不是问题。 请记住,您不必为每个实现class编写装饰器,而是为您关心的每种类型编写装饰器。 理想情况下,每种类型甚至应该是一个interface而不是具体的class

例如,考虑Java Collections Framework类型层次结构。 我们有:

  • interface Collection
    • 它由ListSetQueue等子接口
  • 它还有interface Map不是 Collection

Guava方便地在这种类型层次结构上促进装饰器模式实现:

  • abstract class ForwardingCollection
    • with abstract subclasses ForwardingListForwardingSetForwardingQueue
  • 它还有abstract class ForwardingMap

请注意,没有ForwardingHashMapForwardingTreeSet 。 反正可能没有必要。

也可以看看

  • Effective Java 2nd Edition,Item 18:首选接口到抽象类

相关问题

  • 接口与抽象类(通用OO)
  • 它只是我或接口过度使用?
  • Guava ForwardingList用法示例

如果子类有其他方法,并且您希望通过装饰器访问这些方法,则必须为每个子类编写单独的装饰器。 对于你的特殊问题,我建议另一个解决方案。 从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 } } 

这样,您可以使用不同的方式发送电子邮件与所有类型的电子邮件。

您需要SimpleEmail或HtmlEmail的特殊方法吗?

如果没有,那么非抽象的EmailDecorator就足够了。 如果Email,SimpleEmail或HtmlEmail实现了某些接口,那么你也应该真正实现它们。