Observer mode to explore the depth of tissue taken from the UML Software Engineering: Doug Purdy, Jeffrey Richter
Although design patterns are not Mandan, it is indeed a very powerful tool, developer or architect can use it actively participate in any project. Design patterns ensure common problems with well-known and recognized solutions. The factual basis exists in the pattern is that most problems may already have other individuals or development groups to solve. Therefore, the mode provides a form of sharing can use the solution between developers and organizations. Regardless of the origin of these modes, these models use the knowledge and experience accumulated by everyone. This ensures that the correct code is developed faster and reduces the possibility of errors in design or implementation. In addition, the design model provides general terms between engineering team members. People who participated in large-scale development projects know that using a group of common design terms and guidelines are critical to successfully completing the project. Most importantly, if you can use it correctly, design mode can save you a lot of time. .NET Framework Mode Although the example of GOF is limited to C and SmallTalk, the design pattern is not bundled with a particular language or development platform; the Microsoft .NET framework provides new opportunities and environments for analysis design patterns. Microsoft applies a lot of GOF mode during the development of the framework library (FCL). Some new modes have been developed and proposed due to the wide range of functions provided in the .NET framework. Our research on design patterns starts from Observer mode. An Observer mode is an object-oriented development of an object that is correctly divided in a given application. Each object in the system should focus on discrete abstraction in the problem domain. In short, an object should only be a thing, and it is going to do it well. This approach ensures that a clear limit is defined between objects, thus providing higher reuse and system maintainability. A particularly important area is interaction between user interface and basic business logic. During the development of the application, you need to quickly change the user interface, and you cannot have a joint impact on other parts of the application, which is a matter of empty. In addition, business requirements may also change, and all of this is independent of the user interface. People with rich development experience know that in many cases, these two groups will change. If there is no division of the UI and other parts, modifying any part will adversely affect the overall impact. Many applications need to be divided into clear boundaries between user interfaces and business logic. Therefore, since the GUI appears, many object-oriented frameworks support to divide the user interface from other parts of the application. Most of these applications have almost the same design model. This mode is often referred to as an observer, which is very helpful in dividing clear boundaries between various objects in the system. In addition, this solution is often seen in a frame or application in the frame or application. The role of OBServer mode far exceeds its original idea. Logical model Although the OBServer mode has a lot of variants, the basic premise of this mode contains two roles: Observer and Subject (people who are familiar with SmallTalk MVC, respectively, respectively, respectively as View and Model, respectively.). In the environment of the user interface, the observer is responsible for displaying the data to the user. On the other hand, the body represents the business abstraction from the problem domain. As described in Figure 1, there is a logic association between the observer and the main body. When changing in the main object, (for example, modifying instance variables), the observer will observe this change and update it accordingly.
Figure 1, for example, assuming that we have to develop a simple application to track the full-day stock price. In this application, we specify a Stock class to simulate a variety of stocks in NasDAQ transactions. This class contains an instance variable that represents the share price that frequently fluctuates in different times all day. In order to display this information to the user, the application uses a STOCKDISPLAY class to write information to STDOUT. In this application, a STOCK class instance is used as the body, a StockdisPlay class instance as an observer. As the stock price changes over time in the trading day, the current stock price of the Stock instance will change (how it changes is not important). Because the StockdISPlay instance is observing the Stock instance, these changes will be displayed to the user when these states change (modify the stock price). By using this observation process, you can divide the boundary between the Stock and the StockdisPlay class. Assume that the application's requirements have changed the next day, and the form-based user interface is used. To enable this new feature, you only need to construct a new class STOCKFORM as the observer. No matter what happens, the STOCK class does not need to make any modifications. In fact, it doesn't even know such changes. Similarly, if the demand variation requires the Stock class to retrieve the stock price information from another source (probably from the web service instead of retrieving from the database), the StockDisplay class does not need to be modified. It just continues to observe the stock is enough. The physical model is just like most solutions, the problem is detail. Observer mode is no exception. Although the logical model specifies the observer observing the subject; but when this mode is implemented, this is actually a name misunderstanding. More specifically, the observer is registered with the subject, indicating that it observes the will of the subject. When a certain state changes, the body notifies the observer to this change. When the observer no longer wants to observe the subject, the observer will revoke the main body to the main body. These steps are called observer registrations, notifications, and revoke registrations, respectively. Most frameworks are registered and notified by callbacks. The UML sequence diagram shown in Figures 2, 3 and 4 simulates the objects and method calls commonly used by this method. For those who are unfamiliar with the sequence diagram, the top rectangular frame represents the object, and the arrow indicates method call. Figure 2 Figure 2 depicts a registration sequence. The observer calls the Register method to the main body to transfer itself as a parameter. After receiving this reference, it must store it to notify the observer when a certain time state will change in the future. Most observer implementations are not directly stored in an instance variable, but entrust this task to a separate object (usually one container). Use the container to store the observer instance to provide a very good benefit, we will briefly introduce it.
Figure 3 Figure 3 highlights the notification sequence. When the status changes (askPRICECHANGED, the main body retrieves all the observer in the container by calling the GetobServers method. The main body then enumerates the inspectors of the retrieval, and calls the Notify method to inform the observer's state change.
Figure 4 Figure 4 shows the undo registration sequence. This sequence is performed when the observer no longer needs to observe the subject. The observer calls the Unregister method and transmits itself as a parameter. The main body then calls the container to end the observation process. Go back to our stock app, let us analyze the impact generated by the registration and notification process. During the application startup, a StockDisplay class instance is registered in the Stock instance and passes itself as a parameter to the Register method. STOCK instance (in the container) Save references to the StockDisplay instance. When the stock price attribute changes, the STOCK instance is notified to the StockDisplay notified changes by calling the Notify method. When the application is shut down, the StockdisPlay instance uses the following method to undo the registration instance: call the Unregister method to terminate the relationship between the two instances. Note What is the advantage of using a container (rather than using instance variables) to store observer references. Assume that in addition to the current user interface stockDisplay, we also need to draw the stock price in real-time graphics in the transaction date. To do this, we created a new class called StockGraph, which draws graphics of the share price (Y-axis) and the day (x-axis). When the application is started, it registers instances of the StockDisplay and the STOCKGRAPH class in the Stock instance. Because the body stores the observer in the container (opposite to instance variables), this does not have problems. When the stock price changes, the STOCK instance notifies the occurrence of the state changes to the two observer examples in the container. As we see, using containers can provide greater flexibility, that is, each body can support multiple observes. This makes the body possible to notify the state change to count countless multiple observer, rather than only inform an observer. Although not forced requirements, many frames provide a group of interfaces to be implemented for the observers and entities. As shown in the following C # code example, the IOBSERVER interface discloses a public method Notify. This interface is implemented by all the classes to be used as the observer. The IOBSERVABLE interface (which is disclosed by all the classes to be used as the subject) Register and Unregister. These interfaces typically use the form of an abstract virtual class or a real interface (if you support such a configuration in language). Use these interfaces to help reduce the coupling relationship between the observer and the subject. Unlike the close coupling relationship between the observer and the main class, the IOBSERVER and IOBSERVABLE interfaces allow for independent operations. By analyzing the interface, you will notice that all the methods typed is for the interface type (relative to the specific class). This method extends the advantages of the interface programming model to OBServer mode. IOBSERVER and IOBSERVABLE interface (C #) // Interface The all Observer Classes Should Implement
Public interface IOBSERVER {
Void Notify (Object AnObject);
} // IOBSERVER
// Interface That All Observable Classes Should IMplement
Public interface IOBSERVABLE {
Void Register (IOBSERVER ANOBSERVER);
Void Unregister (IOBSERVER ANOBSERVER);
} // IOBSERVABLE Go back to our sample application, we know that the Stock class is used as the main body. Therefore, it will implement the IOBSERVABLE interface. Similarly, the StockdISPlay class implements the IOBSERVER interface. Because all operations are defined by the interface (rather than by specific class definition), the Stock class is not bound to the StockdisPlay class, and vice versa. This allows us to quickly change specific observers or subjects without affecting other parts of the application (replacing STOCKDISPLAY using different observes). In addition to these interfaces, the framework often provides a general base class for the body to reduce the work required to support the OBServer mode. The base class implements the IOBSERVABLE interface to provide the infrastructure required to support the observer instance storage and notification. The following C # code example briefly introduces a base class named OBSERVABLEIMPL. Although this task may be completed any container, this class entrusts the observer storage to the hash form instance in the Register and Unregister methods (for convenience, we use the hash table in the example as a container, which only uses one Method calls to undo registration specific observers instance). Also pay attention to the added NotifyObservers method. This method is used to inform the observer stored in the hash table. When this method is called, the container will be enumerated and the Notify method is called to the observer instance. ObservableImpl class (C #) // Helper Class That Implements Observable Interface
Public Class ObservableImpl: IOBSERVABLE {
// Container to Store The Observer Instance (is not synchronized for
this esample
Protected hashtable _observercontainer = new hashtable ();
// add the observer
Public void register (IOBSERVER Anobserver) {
_Observercontainer.add (anobserver, anobserver);
} // register
// Remove the observer
Public void unregister (IOBSERVER Anobserver) {
_Observercontainer.remove (anobserver);
} // unregister
// Common Method to Notify All The Observers
Public void notifyobservers (Object Anobject) {
// enumeration the observers and invoke their notify method
Foreach (IOBSERVER ANOBSERVER IN _OBSERVERCONTAINER.KEYS) {
AnobServer.notify; ANOBJECT
} // foreach
} // NotifyObservers
} // OBServableImpl Our sample application uses this method to use this base class infrastructure: Modify the Stock class to extend the OBSERVABLEIMPL class, rather than providing its own specific IOBSERVABLE interface implementation. Because the OBSERVABLEIMPL class implements an IOBSERVABLE interface, there is no need to make any changes to the StockdISPlay class. In fact, this method simplifies the implementation of the OBServer mode, while loosening the coupling relationship between the retaining classes, the same function is used to repeat the same functionality. The following .NET observer example focuses on the use of IOBSERVABLE and IOBSERVER interfaces and OBSERVABLEIMPL classes in our stock applications. In addition to the STOCK and STOCKDISPLAY classes, this example uses MainClass to associate the observer and body instance and modify the AskPrice property of the Stock instance. This property is responsible for calling the NOTIFYOBSERVERS method of the base class, and the method is also notified of the relevant state change to the instance. Observer example (C #) // REPRESENTS A Stock in An Application
Public Class Stock: ObservableImpl {
// Instance Variable for ask Price
Object_askprice;
// Property for ask Price
Public Object askPRICE {
Set {_ASKPRICE = VALUE;
Base.notifyObserve (_ASKPRice);
} // set
} // askPRICE PROPERTY
} // stock
// REPRESENTS THE User Interface in The Application
Public class stockdisplay: IOWOBSERVER {
Public void notify (Object anObject) {
Console.writeline ("The New Ask Price); AnObject);
} // notify
} // StockdISPlay
Public class mainclass {
Public static void main () {
// Create New Display and stock instances
Stockdisplay stockdisplay = new stockdisplay ();
Stock stock = new stock ();
// register the grid
Stock.register (stockdisplay);
// Loop 100 Times and Modify the ask Price
For (int Looper = 0; Looper <100; Looper ) {
stock.askprice = looper;
}
// unregister the display
Stock.unregister (stockdisplay);
} // main
} // The OBServer mode in the MAINCLASS .NET framework is based on our understanding of the Observer mode, let us turn your attention to this mode in the .NET framework. A person who is very familiar with the type of FCL will notice that there is no IOBSERVER, IOBSERVABLE, or OBSERVABLEIMPL type in the frame. Although you can use these constructs in the .NET application, introduce commission and events provide new, powerful methods to implement OBServer mode without developing specific types dedicated to supporting this mode. In fact, because the delegates and events are a first-level member of the CLR, the basic structure of this mode is added to the core of the .NET framework. Therefore, the FCL uses Observer mode in its structure. Introducing a lot of articles in the internal work mode of the entrustment and events, we are here to tell. We only need to explain that the entrustment is a function pointer to object-oriented (and type security) version. The entrustment instance saves a reference to an instance or class method, allowing anonymous to call the binding method. The event is a special structure declared on the class, which can be issued at runtime changes. The event indicates that we are used to implement the form of registration, undo registration and notification methods for the OBServer mode (CLR, and a variety of different compilers support it). The delegate is registered at runtime to a specific event. When incurred events, all registered delegates will be called to enable them to receive the notification of the event. According to the term defined by the OBServer mode, the class that states the event is the main body. Unlike our previously used IOBSERVABLE interfaces and OBSERVABLEIMPL classes, the main class does not need to implement a given interface or extended base class. The body only needs to disclose an event without having to perform any other operations. Observer creation is slightly more, but flexibility has increased much (we will discuss later). The observer does not implement the IOBSERVER interface and register itself into the body, but must create a specific entrustment instance and register this delegate into the main event. Observers must use the entrusted instances of the type specified by the event statement, otherwise, registration will fail. During the creation of this entrustment instance, the observer will pass the method (instance or static) name that the body (instance or static) of the main body is notified. After the delegate is bound to the method, it can be registered in the event. Similarly, registration can also be revoked from the event. The body provides a notification to the observer by calling an event. If you are not familiar with commission and events, implement OBServer mode seems to be a lot of work, especially compared to the IOBSERVERVABLE interface we used. However, it is easier than sounding, and it is easy to achieve much. The following C # and Visual Basic .NET code example focuses on the class modifications required to support the delegate and events in our sample application. Note that no STOCK or STOCKDISPLAY class is used to support any base class or interface of this mode. Observer (C #) using entrusted and events (C #) public class stock {
// Declare a delegate for the event
Public delegate void askPRICEDELEGATE (Object Aprice);
// Declare the Event Using The Delegate
Public Event askPRICEDELEGATE askpricechanged;
// Instance Variable for ask Price
Object_askprice;
// Property for ask Price
Public Object askPRICE {
SET {
// set the installation variable
_ASKPRICE = VALUE;
// fire the event
AskPRICECHANGED (_ASKPRICE);
} // askPRICE PROPERTY
} // stock class
// REPRESENTS THE User Interface in The Application
Public class stockdisplay {
Public void askPRiceChanged (Object Aprice) {
Console.write ("The New Ask Price IS: Aprice " / R / N ");
} // Stockdispslay Class
Public class mainclass {
Public static void main () {
// Create New Display and stock instances
Stockdisplay stockdisplay = new stockdisplay ();
Stock stock = new stock ();
// Create a new delegate instance and bind it
// to the observer's askpriceChanged method
Stock.askpriceDelegate adlegate = new
Stock.askpricedelegate (stockdisplay.askpricechange);
// add the delegate to the event
stock.askpricechanged = adlevation;
// Loop 100 Times and Modify the ask Price
For (int Looper = 0; Looper <100; Looper ) {
stock.askprice = looper;
}
// Remove the delegate from the evenet
stock.askpricechanged- = adlevation;
} // main
} // mainclass After familiar with the entrustments and events, you will clearly see their huge potential. Unlike IOBSERVERVABSERVABLE interfaces and OBSERVABLEIMPL classes, use commission and events can greatly reduce the workload required to implement this mode. The CLR and compiler provide the foundation for the observer container management, and provides a general call convention for registration, undo registration, and notification observer. Perhaps the biggest advantage of the commission is that it can reference any method of inherent characteristics (the condition is it in line with the same signature). This allows any classes to be used as the observer and is independent of the interface it implements the interface or its dedicated class. Although using IOBSERVER and IOBSERVABLE interfaces reduce the coupling relationship between observer and subject classes, use the entrustment to completely eliminate these coupling relationships. The event mode is based on events and delegates, and the FCL can use the OBServer mode very widely. The FCL designer fully recognizes the huge potential of this mode and applies it to the user interface and non-UI-specific functions. However, the usage is slightly different from the basic OBServer mode, and the frame team is called event mode. Typically, this mode is expressed as a formal naming convention for the delegate, event, and related methods involved in the event notification process. Although the CLR or standard compiler does not force all applications and frameworks that use events and commission, Microsoft suggests this. The first conventions may also be the most important agreement that the name of the event disclosed. This name should be self-cleaning for the changes of the status it represent. Remember, this convention, and all other such conventions itself is subjective. The aim is to provide a clear description for those who use your event. Other parts of the event mode use the correct event naming, so this step is critical to the mode. Go back to our example, let us analyze this aggregate impact on the STOCK class. The appropriate method of derived event name is that the name of the field modified in the main class is used as the root. Because the field name modified in the Stock class is _askprice, reasonable event name should be askpricechanged. Obviously, the name of this event is stronger than StateChangeDinstockClass et al. Therefore, the AskPriceChanged event name is in accordance with the first convention. Article 2 of the event mode is the correct naming commission and its signature. The entrustment name should contain event names (through the first agreement) and the additional word Handler. This mode requires commission to specify two parameters, the first parameter provides reference to the event sender, and the second parameter provides environmental information to the observer. The name of the first parameter is Sender. This parameter must be typed as System.Object. This is due to the following facts: may be commissioned to bind any potential methods on any class in the system. The name of the second parameter (even easier than the first parameter) is E. This parameter must be typed to System.Eventargs or some derived class (sometimes more than this). Although the return type of the delegate depends on your implementation, most implementation of this mode does not return any values at all. Need to pay a little attention to the second parameter E of the commission. This parameter allows the main object to pass any environmental information to the observer. If such information is not required, use the System.EventArgs instance is enough because this type of instance represents no environmental data. Otherwise, you should construct the class derived from System.Eventargs to provide this data using the corresponding implementation. This class must be named in accordance with an event name with additional words Eventargs. Please refer to our STOCK class, this agreed requires the delegation to process the AskPriceChanged event askpricechangedhandler. In addition, the second parameter of this delegate should be named asKpriceChangeDeventArgs.
Because we need to pass new stock prices to the observer, we need to extend the System.Eventargs class to name this class askpriceChangeDeventArgs and provide implementation to support this data. The last agreement in the event mode is the name and accessibility of the method of initiating the subject class. The name of this method should include event names and added ON prefixes. The accessibility of this method should be set to protected. This convention is only applicable to non-sealing (inherited in VB), as it is called a known call point for the observer registered in the base class as a derived class. Apply this last convention to the Stock class, you can complete the event mode. Because the Stock class is not sealed, we must add a method to raise an event. According to this mode, the name of this method is onSkPriceChanged. The following C # code sample shows the full view of the event mode applied to the Stock class. Please pay attention to our System.Eventargs class specialized usage. Event Mode Example (C #) Public Class Stock {
// Declare a delegate for the event
Public Delegate Void AskPriceChangedHandler (Object Sender,
AskPRICECHANGEDEVENTARGS E);
// Declare the Event Using The Delegate
Public Event askPRICECHANGEDHANDLER AskpriceChanged;
// Instance Variable for ask Price
Object_askprice;
// Property for ask Price
Public Object askPRICE {
SET {
// set the installation variable
_ASKPRICE = VALUE;
// fire the event
OnaskPriceChanged ();
}
} // askPRICE PROPERTY
// Method to Fire Event Delegate with Proper Name
Protected void OnaskPriceChanged () {
AskPRICECHANGED (this, new askPRiceChangeDeventArgs);
} // askPRICECHANGED
} // stock class
// Specialized Event Class for the askPRICECHANGED EVENT
Public class askPRICECHANGEDEVENTARGS: Eventargs {
// Instance Variable to Store the ask price
Private Object_askprice;
// Constructor That Sets askPRICE
Public askPRiceChangeDeventArgs (Object askprice) {_askprice = askprice;
// public property for the ask price
Public Object askPRICE {get {return_askprice;}}