Вот упрощенный пример шаблона декоратора . Иерархия классов реструктурируется как 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!!!
}
}
Итак, здесь мы имеем простую иерархию Animal
с подклассами Dog
и Cat
. У нас также есть Normal
декоратор - также Animal
- который просто делегирует все методы другому Animal
. То есть, он на самом деле не делает никаких эффективных украшений, но он готов к созданию подкласса, чтобы можно было добавлять фактические украшения.
У нас здесь только один метод, makeNoise()
. Тогда у нас есть два вида реальных украшений, Loud
и Stuttering
. (Рассмотрим случай, когда Animal
имеет много методов; тогда Normal
будет наиболее ценным).
Затем у нас есть метод keepPokingIt(Animal)
, который принимает ЛЮБОЙ Animal
и будет делать с ним ничего, пока он не будет makeNoise()
. В нашей функции main
мы затем keepPokingIt
различных видов животных, украшенных различными чертами личности. Обратите внимание, что мы можем даже укладывать одно украшение поверх другого.
Точные детали реализации могут отличаться, но этот упрощенный пример в значительной степени отражает суть шаблона декоратора.
Другой пример: ForwardingCollection
иерархия из Гуавы
В приведенном выше примере keepPokingIt
заботится только о том, что это Animal
. Иногда вы можете просто нажать Cat
, а не Dog
, или другими способами различить два типа. В таких случаях вы можете указать NormalCat
, NormalDog
и т. Д.
Если вы хорошо спроектируете иерархию типов, это не должно быть проблемой. Помните, что вам не нужно писать декораторы для каждой реализации class
, а по одному для каждого типа , который вас волнует. В идеале каждый тип должен быть interface
, а не конкретным class
.
Рассмотрим, например, иерархию типов Java Collections Framework . У нас есть:
Гуава удобно облегчает реализацию шаблонов декоратора поверх иерархии этого типа:
Обратите внимание, что нет ForwardingHashMap<K,V>
или ForwardingTreeSet<E>
. В любом случае, в этом нет необходимости.
Смотри также
- Effective Java 2nd Edition, Item 18: Предпочитают интерфейсы абстрактным классам
Похожие вопросы