Из Дизайнерской картины Банды Четырех
Почему поля observers
и методы Attach
, Detach
а также Notify
в интерфейсе Subject
в то время как методы GetState
а также SetState
и поле SubjectState
находятся в конкретном подклассе ConcreteSubject
? Почему члены распределены в предметном интерфейсе и конкретном подклассе как они есть?
Почему метод Update
в интерфейсе Observer
в то время как поля subject
а также observerState
в конкретном подклассе ConcreteObserver
?
Почему члены распределены в интерфейсе наблюдателя и конкретном подклассе как они есть?
Кажется, нет симметрии между субъектом и наблюдателем.
Например, почему поле observers
принадлежат интерфейсу Subject
в то время как поле subject
принадлежать ConcreteObserver
?
Благодарю.
Методы GetState и SetState в интерфейсе субъекта сделают интерфейс субъекта зависимым от того, как обновляются зависимые объекты. Это нарушение принципа инверсии зависимости.
Все, что делает предметный интерфейс — это прикрепляет конкретный предмет и уведомляет наблюдателя. То, как изменяются состояния конкретных объектов, не зависит от этого. Это причина, почему getState и setState не являются частью Subject Interface.
Похожая причина, по которой наблюдаемое состояние не является частью интерфейса Observer.
Это шаблон Observer Design. Все паттерны GOF будут максимально использовать принципы ОО. Если обязанности (и состояние) распространены во многих дочерних классах, их необходимо повысить в иерархии базовых классов до самого верхнего возможного уровня. Следующий демо-код и комментарии ответят на вышеуказанные вопросы.
Это правда, что некоторые шаблоны проектирования (и Observer — один из них. См. Приведение типов в update ()) нарушают принципы ОО. Нарушать эти принципы прекрасно, нужно знать, какой принцип и почему мы нарушаем.. и проверить — получаем ли мы больше пользы, чем вероятная негибкость / ущерб, внесенный в реализацию?
public interface Observer {
/**
* IF THIS METHOD IS NOT HERE SUBJECT WILL NOT BE ABLE TO CALL IT
* REFER TO OO BASICS - INHERITANCE
*/
void update(Subject subject);
}
public abstract class Subject {
// THE COMPLETE LOGIC HERE IS INDEPENDENT OF
// WHO THE CONCRETE SUBJECT IS.
// HENCE THIS IS A RIGHT PLACE FOR IT AS PER OO PRINCIPLES
// NOTE THAT THIS CLASS KNOWS ONLY ABOUT ABSTRACT OBSERVER
private Set<Observer> observers;
public void add(Observer o){/*...*/}
public void remove(Observer o){/*...*/}
protected void notifyAllObservers(){
for (Observer observer : observers) {
observer.update(this);
}
}
}
class ConcreteSubject extends Subject {
/**
* HERE THERE IS NO NEED TO KNOW THE CONCRETE OBSERVER-
* By DEFINITION OF THIS PATTERN.
* THE JOB HERE IS TO ONLY NOTIFY ALL OBSERVERS THAT "I HAVE CHANGED"* ITS OBSERVER'S RESPONSIBILITY TO REACT IN PULL METHOD
*/
// Concrete Subject state
// Concrete Subject behavior
public void specificMethod(){
//... concrete class specific logic
}
}
class ConcreteObserver implements Observer{
@Override
public void update(Subject subject) {
/**
* OBSERVE HERE WE NEED TO TYPE CAST, WHICH LEADS TO VIOLATION OF
* OO PRINCIPLES. BUT WE GET BENEFIT OF LOOSELY COUPLED OBSERVERS
* AGAINST THIS EXTRA COST
* ITS SAFE TO VIOLATE HERE AS OBSERVER INSTANCES MAKE ONLY
* SENSE IN CONTEXT WITH SUBJECT AND WILL NEVER BE REUSED AS
* OBSERVERS IN UNRELATED CONTEXT.
*/
((ConcreteSubject)subject).specificMethod();
}
}