1. Background
This content is from the second chapter of the book Head First Design Patterns, Observer Patterns (let the object know what you are doing)
2. OO Base
- encapsulation
- inherit
- polymorphic
- abstract
3. OO Design Principles
- Encapsulate changes, encapsulate changed parts, separate from fixed parts
- Interface-oriented programming, not implementation-oriented programming
- Combination is better than inheritance
- Working for loose coupling between interactive objects
4. Understanding the Observer Model
1. Definition
Observer mode - Defines a one-to-many relationship between objects so that when the state of an object's data changes, other objects that depend on it can be aware of the change in the data.
2.UML Diagram
3. Loose coupling
The observer mode provides an object design that loosely couples the subject and the observer (two objects can still interact but do not know each other's details).
Because a theme doesn't know everything about the observer, it only knows that the observer implements an interface. A theme doesn't need to know who the observer is, what it does, or any other detail. Without changing the theme, we can increase or decrease the number of observers as much as we want it to implement an interface. The theme's job is that when the state changesThe data is sent to the observer through a protocol.
A loosely coupled design allows us to build a flexible system because it minimizes the dependencies between objects.
5. Design of Weather Stations
1. Push in observer mode
a.UML Diagram
b. Code cases
(1) Subject:
// subject public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); } // concreteSubject public class WeatherData implements Subject { private List<Observer> observers; private float temperature; private float humidity; private float pressure; public WeatherData(){ observers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0){ observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(temperature,humidity,pressure); } } public void measurementsChanged(){ notifyObservers(); } public void setMeasurement(float temperature,float humidity,float pressure){ this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
(2) Observers:
public interface Observer { void update(float temperature,float humidity,float pressure); } //Here the specific observer shows only one, and the other observer implementations in the UML class diagram are the same public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { //Set up the data needed for the display board this.temperature = temperature; this.humidity = humidity; //Show display(); } @Override public void display() { System.out.println("Current conditions: "+temperature+"F degrees and "+humidity+"% humidity"); } }
2. Pull in observer mode
a.UML
b. Code cases
Pull is done using the Java Content Observer mode, the theme inherits java.util.Observable, and the observer implements java.util.Observer
public class WeatherDataV2 extends Observable { private float temperature; private float humidity; private float pressure; public WeatherDataV2() { } public void measurementsChanged() { setChanged(); notifyObservers(); } public void setMeasurements(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } } // Other observers implement the same principle without repeating the paste. For the DisplayElement interface, it's the same as Push public class CurrentConditionsDisplay implements Observer,DisplayElement { Observable observable; private float temperature; private float humidity; public CurrentConditionsDisplay(Observable observable){ this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherDataV2){ WeatherDataV2 weatherDataV2 = (WeatherDataV2) o; this.temperature = weatherDataV2.getTemperature(); this.humidity = weatherDataV2.getHumidity(); } } @Override public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } }
3. Advantages and disadvantages of Push-Pull
(1) Advantage of push: it informs the observer of all the data, which reduces the complexity of obtaining data by the observer
(2) The disadvantage of push: When we need to modify parameters (data), such as adding several new indicators, then we have to modify the protocol, update the number of parameters in the Observer interface, and modify each of its subclasses once.
(3) The advantage of "pull": there is no need to worry about adding or reducing parameters to the theme, and the observer takes the initiative to get the data he needs.
(4) Disadvantage of "pull": getting data is tedious
In fact, the advantages and disadvantages of the two are opposite, making up for the disadvantage of both sides, and worrying about the inevitable bring their own disadvantage. Generally speaking, people think the "push" way is correct (in the book).
6. Summary
1. The observer pattern follows design principles
- Encapsulate changes, encapsulate the changing parts, and separate them from the fixed ones. In observer mode, what changes is the state of the subject, as well as the number and type of observers. With this mode, we can change objects that depend on the subject without modifying the subject.
- Programming for interfaces, not for implementations. Both themes and observers use interfaces, and observers use interfaces for themes to register with them, while themes use observer's interfaces to notify observers of data, so they don't have to care about each other's implementation details to achieve loose coupling.
- Combination is better than inheritance. The observer mode registers the observer as a theme by using "grouping". Relationships between objects are not achieved through inheritance, but through grouping at run time.
- Efforts to loosely couple the interactive objects. Same as 2
2. Summary of some key points of the observer model
- The observer pattern defines a one-to-many dependency.
- Themes (that is, observers) update observers with a common interface.
- The loosely coupled way between the observer and the observer allows the observer to not know the observer's details but only know that the observer has implemented the observer interface.
- With this model, we can get data by "pushing" or "pulling" from the observer.
- When there are multiple observers, you cannot rely on a specific order of notifications.
- Java has many implementations of observer modes, including the generic java.util.Observable. Note the problems with the java.util.Observable implementation because it changes the order in which observers are notified. We can implement our own Observable if necessary, but it is not difficult.
- Many GUI frameworks, javaBeans, RMI use the Observer mode.