#java #oop #design-patterns #observer-pattern #template-method-pattern
#java #ооп #шаблоны проектирования #наблюдатель-шаблон #шаблон-метод-шаблон
Вопрос:
Я реализую шаблон наблюдателя следующим образом:
interface Layer{
void adjustString(Set<String> strings);
}
interface NotifiableLayer extends Layer{
void layerAdjusted(Layer layer);
}
abstract class ObservableLayer implements Layer{
Set<NotifiableLayer> observers = new HashSet<>();
void addObserver(NotifiableLayer layer){
observers.add(layer);
}
void removeObserver(NotifiableLayer layer){
observers.remove(layer);
}
void notifyObservers(){
observers.forEach(l -> l.layerAdjusted(this));
}
}
class MyLayer extends ObservableLayer{
@Override
public void adjustString(Set<String> strings) {
this.notifyObservers(); //can this be auto?
}
}
И это, конечно, работает, но тот, кто реализует ObservableLayer
, должен помнить о вызове this.notifyObservers()
adjustString
метода. Это не имеет большого значения, но я хотел посмотреть, есть ли способ полностью скрыть это.
Пока у меня есть только эта идея (с использованием метода шаблона):
abstract class ObservableLayer implements Layer{
//...methods removed for simplicity
@Override
public void adjustString(Set<String> strings) {
this.doAdjustString(strings);
this.notifyObservers(); //<---- here is auto
}
abstract void doAdjustString(Set<String> strings);
}
class MyLayer extends ObservableLayer{
@Override
public void doAdjustString(Set<String> strings) {
//now notification is in base adjustString
}
}
но здесь мне не нравится, что имя метода изменено на doAdjustString
, и оно больше не является единообразным для других реализаций уровня (слоев, которые непосредственно реализуют Layer
интерфейс).
Есть ли какой-нибудь простой способ получить эту функциональность, но сохранить public void adjustString(Set<String> strings)
подпись в MyLayer
классе?
Ответ №1:
Одним из способов было бы использовать экземпляр декоратора, который содержит ObservableLayer
экземпляр и делегирует ему.
final class LayerDecorator implements Layer {
final private ObservableLayer delegate;
public LayerDecorator(ObservableLayer delegate) {
this.delegate = delegate;
}
@Override
public void adjustString(Set<String> strings) {
delegate.adjustString(strings);
delegate.notifyObservers();
}
}
Это предполагает, что вызывающий код работает с использованием ссылок на Layer
вместо ObservableLayer
.
Если вызывающий код должен работать с использованием ссылок на ObservableLayer
, то, возможно, лучше провести рефакторинг ObservableLayer
, чтобы быть интерфейсом, имеющим методы для регистрации слушателей, их удаления и уведомления. Этот интерфейс также расширяет Layer
интерфейс.
interface IObservableLayer extends Layer {
void addObserver(NotifiableLayer layer);
void removeObserver(NotifiableLayer layer);
void notifyObservers();
}
Абстрактный класс ObservableLayer
изменяется на IObservableLayer
implementate вместо Layer
прямого. Этот класс остается открытым для поддержки классов приложений для определения вариантов наблюдаемых слоев.
Далее можно определить внутренний декоратор для наблюдаемых слоев, как показано ниже.
final class ObservableLayerDecorator implements IObservableLayer {
final private ObservableLayer delegate;
public ObservableLayerDecorator(ObservableLayer delegate) {
this.delegate = delegate;
}
@Override
public void addObserver(NotifiableLayer layer) {
delegate.addObserver(layer);
}
@Override
public void removeObserver(NotifiableLayer layer) {
delegate.removeObserver(layer);
}
@Override
public void notifyObservers() {
delegate.notifyObservers();
}
@Override
public void adjustString(Set<String> strings) {
delegate.adjustString(strings);
this.notifyObservers();
}
}
Пожалуйста, обратите внимание, как выполняется уведомление в этом случае.
Теперь экземпляры IObservableLayer
могут быть созданы как
IObservableLayer observableLayer = new ObservableLayerDecorator(new MyClass());
Здесь будут полезны фабричные методы, поскольку их можно определить для обработки создания различных классов наблюдаемых слоев уровня приложения, чтобы можно было последовательно создавать экземпляры, возвращающие IObservableLayer
значение, которое оформлено. Это освободит разработчиков от необходимости знать, как использовать декоратор, и позволит декоратору быть внутренней утилитой.
Комментарии:
1. Спасибо за еще один подход — стандартный «предпочтение композиции перед наследованием».
Ответ №2:
Другой подход — аспектно-ориентированное программирование.
Следующий пример использует AspectJ для перехвата любого public
выполнения метода в расширяющемся классе Observable
и вызова notifyObservers()
для того же объекта.
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EventAspect {
@AfterReturning("execution(public * Observable.*(..)) amp;amp; target(observable)")
public void notifyObservers(Observable observable) {
observable.notifyObservers();
}
}