Вот упрощенный пример шаблона декоратора. Иерархия классов реструктурирована как 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. У нас есть:
Guava удобно упрощает реализацию шаблона декоратора поверх этой иерархии типов:
Обратите внимание, что нет ни ForwardingHashMap<K,V>
, ни ForwardingTreeSet<E>
. В любом случае, наверное, в этом нет необходимости.
Смотрите также
- Эффективное второе издание Java, пункт 18: предпочитайте интерфейсы абстрактным классам
Связанные вопросы
person
polygenelubricants
schedule
24.08.2010