Overview
It is used to establish a dependency between objects and objects that are automatically notified when an object changes, and other objects respond accordingly. In the Observer pattern, the object of the change is called the observation target, and the object being notified is called the Observer, and an observation target can correspond to multiple observers, and there can be no interaction between the observers, and the observer can be added and removed as needed, making the system easier to expand.
Observer pattern (Observer pattern): Defines a one-to-many dependency between objects, so that whenever an object state changes, its dependent objects are notified and automatically updated. The alias of the Observer pattern includes the publish-subscribe (publish/subscribe) mode, the Model-view (Model/view) mode, the source-listener (Source/listener) mode, or the slave (dependents) mode. The Observer pattern is an object-behavioral pattern.
Observer pattern Structure
The observer pattern structure usually includes the observation target and the observer two inheritance hierarchies, its structure:
The following roles are included in the Observer Pattern structure diagram:
Subject (target): The target is also called the subject, which refers to the object being observed. A set of observers is defined in the target, and an observation target can be observed by any number of observers, which provides a series of methods for adding and deleting the Observer object, and it defines the notification method notify (). The target class can be an interface, or it can be an abstract class or a concrete class.
ConcreteSubject (target): The target is a subclass of the target class, which usually contains data that changes frequently, notifies its observers when its state changes, and also implements the abstract business logic method, if any, defined in the target class. If you do not need to extend the target class, the target class can be omitted.
OBSERVER (Observer): The Observer will respond to changes in the observation target, which is generally defined as an interface that declares the method update () that updates the data, and therefore is also known as an abstract observer.
Concreteobserver (Specific observer): maintains a reference to a specific target object in a specific observer, which stores the state of the specific observer, which needs to be consistent with the state of the target, which implements the update defined in the Abstract Observer Observer ( Method Typically, when implemented, you can invoke the attach () method of a specific target class to add itself to the collection of target classes or to remove itself from the collection of target classes by means of the detach () method.
The Observer pattern describes how to establish a dependency between objects and objects, and how to construct a system that satisfies this requirement. The observer pattern consists of two types of objects, the observer and the Observer, and a target can have any number of observers that depend on it, and all observers will be notified once the state of the observed target changes. As a response to this notification, each observer will monitor the state of the observed target to synchronize its state with the target State, which is also known as a publish-subscribe (publish-subscribe). The observation target is the publisher of the notification, which does not need to know who the observer is, and can have any number of observers subscribe to it and receive notifications.
Observer pattern Typical Code
This pattern is further analyzed by the schematic code below. First we define an abstract target subject, the typical code is as follows
ImportJava.util.*; Abstract classSubject {//defines an observer collection for storing all observer objectsprotectedArrayList observers<observer> =NewArrayList (); //registration method for adding an observer to the Observer Collection Public voidAttach (Observer Observer) {observers.add (Observer); } //Logoff method for deleting an observer in the Observer Collection Public voidDetach (Observer Observer) {observers.remove (Observer); } //declaring an abstract notification method Public Abstract voidnotify (); }
The target class ConcreteSubject is a concrete subclass that implements the abstract target class subject, and its typical code is as follows:
class extends Subject { // Implement notification method public void notify () { // traverse the Observer collection, invoking the response method for each observer for (Object obs:observers) { ((Observer) obs). Update (); }}
The abstract observer role is generally defined as an interface, typically declaring only one update () method, defining the same interface for the update (response) behavior of different observers, which is implemented in its subclasses and different observers have different response methods. The typical code for the abstract Observer Observer is as follows:
Interface Observer { // Declaration Response Method public void update (); }
The update () method is implemented in the specific observer concreteobserver, and its typical code is as follows:
class Implements Observer { // Implement response method public void Update () { // Specific response code }
In some more complex cases, the update () method of the specific observer class Concreteobserver needs to be executed with the state (attribute) in the target class ConcreteSubject. So there is sometimes an association or dependency between Concreteobserver and ConcreteSubject, and a ConcreteSubject instance is defined in Concreteobserver, This instance gets the state stored in the ConcreteSubject. If the Concreteobserver update () method does not need to use the state attribute in ConcreteSubject, then the standard structure of the observer pattern can be simplified, There is no need to maintain object references between specific observer Concreteobserver and specific target concretesubject. If there is correlation in the specific layer, the scalability of the system will be affected, adding new target classes sometimes need to modify the original observer's code, to a certain extent violates the "open and close principle", but if the original observer class does not need to correlate the new target, then the system extensibility is not affected.
Instance Application
Software companies to develop a multiplayer online game (similar to World of Warcraft, StarCraft and other games), in the game, multiple players can join the same team to form a league, when a member of the team will be attacked by the enemy to send a notification to all other allies, the Allies will respond after receiving notification.
In Figure 22-4, Allycontrolcenter acts as the target class, Concreteallycontrolcenter acts as the target class, observer acts as an abstract observer, and the player acts as a specific observer. The complete code looks like this:
ImportJava.util.*;//abstract Observation ClassInterfaceObserver { PublicString getName (); Public voidsetName (String name); Public voidHelp ();//declaring a support ally method Public voidBeattacked (Allycontrolcenter ACC);//declaring a compromised method}//team members Category: Specific Observer classclassPlayerImplementsObserver {PrivateString name; PublicPlayer (String name) { This. Name =name; } Public voidsetName (String name) { This. Name =name; } PublicString GetName () {return This. Name; } //support the implementation of the Ally method Public voidHelp () {System.out.println ("Hold on," + This. Name + "To save you!" "); } //The implementation of the attack method will call the team control center class notification Method Notifyobserver () to notify the Allies when the attack occurs Public voidbeattacked (Allycontrolcenter acc) {System.out.println ( This. Name + "attacked!" "); Acc.notifyobserver (name); }}//Squad Control Center class: Target classAbstract classAllycontrolcenter {protectedString Allyname;//Squad name protectedArraylist<observer> players =NewArraylist<observer> ();//define a collection for storage team members Public voidsetallyname (String allyname) { This. Allyname =Allyname; } PublicString Getallyname () {return This. Allyname; } //Registration Method Public voidJoin (Observer obs) {System.out.println (Obs.getname ()+ "Join" + This. allyname + "Squad! "); Players.add (OBS); } //Logout Method Public voidquit (Observer obs) {System.out.println (Obs.getname ()+ "Exit" + This. allyname + "Squad! "); Players.remove (OBS); } //declaring an abstract notification method Public Abstract voidnotifyobserver (String name);}//Specific Squad control Center Category: Target classclassConcreteallycontrolcenterextendsAllycontrolcenter { PublicConcreteallycontrolcenter (String allyname) {System.out.println (Allyname+ "Team formation success!" "); System.out.println ("----------------------------"); This. Allyname =Allyname; } //Implementing Notification Methods Public voidnotifyobserver (String name) {System.out.println ( This. Allyname + "Squad emergency notification, allies" + name + "attack by enemy!" "); //iterate through the Observer collection, invoking the support method of each ally (excluding itself) for(Object obs:players) {if(!( (Observer) obs). GetName (). Equalsignorecase (name)) {((Observer) obs). Help (); } } }}
Write the following client test code:
classClient { Public Static voidMain (String args[]) {//define observation target objectAllycontrolcenter acc; ACC=NewConcreteallycontrolcenter ("Jin Yong Group Man"); //define four observer objectsObserver player1,player2,player3,player4; Player1=NewPlayer ("Yang Too"); Acc.join (Player1); Player2=NewPlayer ("Make Fox Punch"); Acc.join (PLAYER2); Player3=NewPlayer ("Zhang Mowgli"); Acc.join (PLAYER3); Player4=NewPlayer ("Duan Yu")); Acc.join (PLAYER4); //a member is under attackplayer1.beattacked (ACC); } }
Compile and run the program with the following output:
金庸群侠战队组建成功!----------------------------杨过加入金庸群侠战队!令狐冲加入金庸群侠战队!张无忌加入金庸群侠战队!段誉加入金庸群侠战队!杨过被攻击!金庸群侠战队紧急通知,盟友杨过遭受敌人攻击!坚持住,令狐冲来救你!坚持住,张无忌来救你!坚持住,段誉来救你!
In this example, the linkage between two objects is implemented, and when the beattacked () method of a gamer player object is called, the Allycontrolcenter Notifyobserver () method is called to process it. In the Notifyobserver () method, the Help () method of the other player object is called. The player's beattacked () method, the Allycontrolcenter Notifyobserver () method, and the player's Help () method form a linkage trigger chain, which is executed in the following order:
Player.beattacked () –> allycontrolcenter.notifyobserver () –>player.help ().
JDK support for observer patterns
The position of the observer pattern in the Java language is very important. In the JDK's java.util package, the observable class and the observer interface are provided, which form the JDK support for the Observer pattern. 22-5 is shown below:
(1) Observer interface
Only one method is declared in the Java.util.Observer interface, which acts as an abstract observer whose method declaration code is as follows:
void Update (Observable o, Object Arg);
When the state of the observed target changes, the method is called, and the update () method is implemented in the subclass of Observer, where the specific observer can have different update behavior as needed. When you call the Notifyobservers () method that observes the target class observable, the update () method in the Observer class is executed.
(2) Observable class
The Java.util.Observable class acts as an observation target class, and in observable defines a vector vector to store the Observer object, which contains the methods and descriptions shown in table 22-1:
Table 22-1 methods and descriptions of the observable class
Observable ()
Constructs a method that instantiates vector vectors.
Addobserver (Observer o)
Used to register a new observer object into the vector.
Deleteobserver (Observer o)
Used to delete one of the observer objects in a vector.
Notifyobservers () and notifyobservers (Object Arg)
Notification method used to loop the update () method of each observer in the call vector inside a method.
Deleteobservers ()
Used to empty the vector, that is, to delete all the observer objects in the vector.
Setchanged ()
When the method is called, a boolean-type internal tag variable changed is set to true, indicating that the state of the observed target object has changed.
Clearchanged ()
Used to set the value of the changed variable to false to indicate that the state of the object is no longer changing or that all observer objects have been notified, and their update () method is called.
HasChanged ()
Used to test whether the object state has changed.
Countobservers ()
Used to return the number of observers in a vector.
We can use the Observer interface and the observable class directly as the abstraction layer of the observer pattern, then customize the specific observer class and the specific observation target class, by using the Observer interface and the observable class in the JDK, Observer patterns can be more easily applied in the Java language.
Observer patterns and Java event handling
The event model for JDK 1.0 and earlier is based on the responsibility chain pattern, but this model does not apply to complex systems, so in JDK 1.1 and later versions, the event-handling model uses the Observer-mode-based delegated event model (Delegationevent model, DEM), That is, an event raised by a Java component is not handled by the object that raised the event itself, but instead is delegated to the independent event-handling object.
In a DEM model, a target role (such as an interface component) is responsible for publishing an event, and the Observer role (the event handler) can subscribe to the event that it is interested in to the target. When a specific target produces an event, it notifies all subscribers. The publisher of the event is called the event source, and the Subscriber is called the event Listener, which in the process can also pass event-related information through the event object. Event handlers can be implemented in the implementation class of an event listener, so an event listener can also be called an event-handling object. The event source object, the event listener object (the event handler object), and the event object form the three elements of the Java event Processing model. The event source object acts as an observation target, while the event listener acts as an observer. As an example of a button click event, its event processing flow is as follows:
(1) If a user clicks a button in the GUI to trigger an event (such as an action event of type ActionEvent), the JVM will produce an event object of the corresponding ActionEvent type, where the event object contains information about the event and event source, and the button is the event source object ;
(2) The ActionEvent event object is passed to the event listener (event handler object), and the JDK provides an interface ActionListener dedicated to handling the ActionEvent event. The developer needs to provide a ActionListener implementation class (such as Myactionhandler) that implements the abstract event-handling method declared in the ActionListener interface actionperformed (), and handles the occurrence of the event accordingly;
(3) The developer registers the implementation class (such as Myactionhandler) object of the ActionListener interface with the button, which can be realized by the addActionListener () method of the Button class;
(4) The JVM invokes the Firexxx () method of the button when triggering an event, and within that method invokes the Actionperformed () method of the event-handling object registered to the button to implement the handling of the event.
Using a similar approach, we can customize GUI components, such as the login component Loginbean with two text boxes and two buttons, with a design that can be shown in 22-6:
The related classes in Figure 22-6 are described below:
(1) Loginevent is an event class that encapsulates information about an event, which is not part of the observer pattern, but it can pass data between the target object and the Observer object, in the AWT event model, All custom event classes are subclasses of Java.util.EventObject.
(2) Logineventlistener acts as an abstract observer, declaring the event response method Validatelogin (), which is used to handle events, also known as event handling methods, Validatelogin () The Loginevent method uses an event object of the type of the event as an argument for transmitting the data related to the events, implementing the method in its subclasses, and implementing the specific event handling.
(3) Loginbean acts as a target class, where we do not define abstract target classes and simplify the observer pattern. Object Lel and Event object loginevent with abstract observer Logineventlistener type defined in Loginbean, with registration Method Addlogineventlistener () for adding observers, in Java event handling, It is common to use one-to-one observer patterns, rather than a one-to-many observer pattern, which means that an observer object is defined only by a single observer, rather than providing a collection of observer objects. The notification method Fireloginevent () is also defined in Loginbean, which is called the "ignition method" in the Java event Processing model, and an event object loginevent is instantiated inside the method, passing the user input information to the Observer object. and called the Observer object's response Method Validatelogin ().
(4) Loginvalidatora and Loginvalidatorb act as specific observer classes that implement the abstract method Validatelogin () declared in the Logineventlistener interface to implement event handling specifically. The method contains a parameter of type loginevent, which can provide different implementations for the same event in the Loginvalidatora and Loginvalidatorb classes.
Observer Patterns and MVC
In the current popular MVC (Model-view-controller) architecture, the Observer pattern is also applied, and MVC is an architectural pattern that contains three roles: model, view, and controller. Where the model corresponds to the observed target in the observer pattern, and the view corresponds to the Observer, the Controller can act as a mediator between the two. When the data in the model layer changes, the view layer automatically changes its display content. 22-7 is shown below:
In Figure 22-7, the data provided by the model layer is the object observed by the view layer, including two chart objects for displaying data in the view layer, a histogram, a pie chart, the same data having different chart displays, and if the data of the model layer changes, two of the chart objects will change, This means that the chart object relies on the data objects provided by the model layer, so any state changes to the data objects should be notified immediately. At the same time, the two charts are independent of each other, there is no connection, and there is no limit to the number of chart objects, users can add new chart objects, such as line charts, as needed. When adding a new chart object, it is not necessary to modify the original class library to meet the "open and close principle".
Summarize
Observer mode is a very high-frequency design pattern, whether it is mobile applications, Web applications or desktop applications, the observer pattern is almost ubiquitous, it provides a complete set of solutions for the linkage between objects, which involves one-to-one or one-to-many object interaction scenarios can use the Observer pattern. Observer patterns are widely used in the implementation of GUI event processing in various programming languages, and observer patterns are used in event-based XML parsing techniques such as SAX2 and Web event processing.
The advantages of the observer pattern
The main advantages of the observer pattern are as follows:
(1) The observer pattern can realize the separation of the presentation layer and the data logic layer, defines a stable message update delivery mechanism, and abstracts the update interface, so that there can be a variety of different presentation layers to act as the specific observer role.
(2) The observer pattern creates an abstract coupling between the observation target and the observer. Observing a target requires only maintaining a collection of abstract observers, without having to know its specific observer. Since observation targets and observers are not tightly coupled, they can belong to different levels of abstraction.
(3) The Observer mode supports broadcast communication, and observing the target sends notifications to all registered observer objects, simplifying the difficulty of a one-to-many system design.
(4) The observer pattern satisfies the requirement of "open and close principle", adding the new observer does not need to modify the original system code, and it is convenient to add new observation target when there is no correlation between the specific observer and the observation target.
The disadvantage of the observer pattern
The main disadvantages of the observer pattern are as follows:
(1) If a target object has many direct and indirect observers, all observers are informed that it will take a lot of time.
(2) If there is a cyclic dependency between the observer and the observation target, observing the target triggers a cyclic call between them, which can cause the system to crash.
(3) The Observer pattern does not have a mechanism for the observer to know how the target object is changing, but only to know that the observation target has changed.
Observer pattern for scenarios
You can consider using the observer pattern in the following situations:
(1) An abstract model has two aspects, one of which relies on another, encapsulating the two aspects in separate objects so that they can be independently changed and reused.
(2) Changing an object will cause one or more other objects to change, without knowing exactly how many objects will change or who they are.
(3) A trigger chain needs to be created in the system, the behavior of the A object will affect the B object, and the behavior of the B object will affect the C object ..., you can use the observer pattern to create a chain-triggered mechanism.
Design Pattern Summary (Java)--Viewer mode