查看以下(简化)类的层次结构:
> Email (base class)
> SimpleEmail extends Email
> HtmlEmail extends Email
我需要修饰Email.send()以添加限制功能。 我需要实例化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
我的大脑并没有围绕如何正确处理我需要“增强”的基类的重要现有子类。大多数示例都将其简化为继承问题变得混乱的一个点。
答案 0 :(得分:9)
以下是decorator pattern的简化示例。类层次结构重组为static
内部类,以便整个示例包含在一个编译单元(as seen on 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!!!
}
}
所以这里我们有一个简单的Animal
层次结构,包含Dog
和Cat
个子类。我们还有Normal
装饰器 - 也是Animal
- 它只是将所有方法委托给另一个Animal
。也就是说,它并没有真正做任何有效的装饰,但它已经准备好进行子类化,以便可以添加实际的装饰。
我们这里只有一个方法makeNoise()
。然后,我们有两种实际装饰,Loud
和Stuttering
。 (考虑Animal
有多种方法的情况;然后Normal
最有价值。
然后我们有一个keepPokingIt(Animal)
方法,该方法需要 ANY Animal
,并且会在makeNoise()
之前对其进行无法控制的事情。在我们的main
函数中,我们然后keepPokingIt
各种各样的动物,装饰着各种个性特征。请注意,我们甚至可以堆叠一个装饰在另一个装饰之上。
确切的实现细节可能会有所不同,但这个简化的示例几乎捕获了装饰器模式的本质。
ForwardingCollection
层次结构在上面的例子中,keepPokingIt
只关心它是Animal
。有时您可能只想戳Cat
而不是Dog
,或以其他方式区分这两种类型。在这种情况下,您可以提供NormalCat
,NormalDog
等
如果你很好地设计你的类型层次结构,这应该不是问题。请记住,您不必为每个实现class
编写装饰器,而是为您关心的每个类型编写一个装饰器。理想情况下,每种类型甚至应该是interface
而不是具体的class
。
例如,考虑Java Collections Framework类型层次结构。我们有:
Guava可以方便地在此类型层次结构上实现装饰器模式实现:
请注意,没有ForwardingHashMap<K,V>
或ForwardingTreeSet<E>
。反正可能没有必要。
答案 1 :(得分:1)
如果子类有其他方法,并且您希望通过装饰器访问这些方法,则必须为每个子类编写单独的装饰器。对于您的特殊问题,我建议另一种解决方案。从send
- 类中删除Email
- 方法,并创建一个负责发送电子邮件的新类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
}
}
通过这种方式,您可以使用不同的方式发送包含所有类型电子邮件的电子邮件。
答案 2 :(得分:0)
您需要SimpleEmail或HtmlEmail的特殊方法吗?
如果没有,那么非抽象的EmailDecorator就足够了。如果Email,SimpleEmail或HtmlEmail实现了某些接口,你也应该真正实现它们。