将装饰器设计模式用于类的层次结构
查看以下(简化)类的层次结构:
> 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
层次结构,带有Dog
和Cat
子类。 我们还有一个Normal
装饰器 – 也是一个Animal
– 它只是将所有方法委托给另一个Animal
。 也就是说,它并没有真正做任何有效的装饰,但它已准备好进行子类化,以便可以添加实际的装饰。
我们这里只有一个方法, makeNoise()
。 然后我们有两种实际装饰, Loud
和Stuttering
。 (考虑Animal
有很多方法的情况;那么Normal
会是最有价值的)。
然后我们有一个keepPokingIt(Animal)
方法,该方法接受任何 Animal
,并且在makeNoise()
之前makeNoise()
它做任何 makeNoise()
事情。 在我们的main
function中,我们随后keepPokingIt
各种各样的动物,装饰着各种人格特质。 请注意,我们甚至可以将一个装饰堆叠在另一个上面。
确切的实现细节可能会有所不同,但这个简化的示例几乎捕获了装饰器模式的本质。
另一个例子:来自Guava的ForwardingCollection
层次结构
在上面的例子中, keepPokingIt
只关心它是一个Animal
。 有时您可能只想戳Cat
而不是Dog
,或者以其他方式区分这两种类型。 在这种情况下,您可以提供NormalCat
, NormalDog
等。
如果你很好地设计你的类型层次结构,这应该不是问题。 请记住,您不必为每个实现class
编写装饰器,而是为您关心的每种类型编写装饰器。 理想情况下,每种类型甚至应该是一个interface
而不是具体的class
。
例如,考虑Java Collections Framework类型层次结构。 我们有:
-
interface Collection
- 它由
List
,Set
,Queue
等子接口
- 它由
- 它还有
interface Map
( 不是Collection
)
Guava方便地在这种类型层次结构上促进装饰器模式实现:
-
abstract class ForwardingCollection
- with
abstract
subclassesForwardingList
,ForwardingSet
,ForwardingQueue
- with
- 它还有
abstract class ForwardingMap
请注意,没有ForwardingHashMap
或ForwardingTreeSet
。 反正可能没有必要。
也可以看看
- 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实现了某些接口,那么你也应该真正实现它们。