Observer design pattern

xiaoxiao2021-03-06  41

Although GOF limits their examples in C and SmallTalk, design patterns are not bundled with a given language or development platform; Microsoft .NET framework has provided new opportunities and environments for analysis design patterns. During the development of the framework library (FCL), Microsoft applied a lot of GOFs to be presented in 1994. Some new modes have been developed and proposed due to the wide range of functions provided in the .NET framework.

In this series of articles, we will detail some of the design patterns involved in the FCL. We will consider the general structure and advantages of each pattern, then introduce specific implementations in the FCL. Although we introduce most modes from GOF, the .NET framework provides a lot of novel features, there is currently no or have little design principles. We will also introduce design patterns related to these new features. Our research on design patterns from the observer mode.

Observer mode

One dominant principle for object-oriented development is that tasks are allocated correctly in a given application. Each object in the system should focus on discrete abstraction in the problem domain, not in any other aspect. 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 for a correct division task is that the interaction between user interfaces and basic business logic. During the development of the application, you need to quickly change the user interface requirements, and will not have a joint impact on other parts of the application, which is a matter of exemption. 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 will encounter the following frequent problems: need to divide clear boundaries between user interfaces and business logic. Therefore, many object-oriented frameworks developed since the GUI have supported it to divide the user interface from other parts of the application. Don't surprise (possibly a little), most of which use similar design modes to provide this feature. This model is often referred to as an observer, which is very advantageous in terms of clear limits between various objects in the system. In addition, this solution is often seen in a frame or application in the frame or application. As most other modes, the role of the observer model far exceeds its original idea.

Logical model

Although the observer model has a lot of variants, the basic premise of this model consists of two roles: the observer and subject (familiar with the SMALLTALK MVC, respectively, respectively, respectively, 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. Observer and subject relationship

For example, assuming that we have to develop a simple application to track the full-day stock price. In this application, we specify a "common" class to simulate a variety of stocks in NasDAQ transactions. This class contains an instance variable that represents the current inquiry that frequently fluctuates at 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 "common" class instance is used as the body, and a STOCKDISPLAY class instance is an observer. As the inquiry changes over time in the trading day, the current inquiry of the "common" instance changes (how it changes is not important). Because the STOCKDISPLAY instance is observing the "common" instance, these changes will be displayed to the user when these states change (modify the inquiry). By using this observation process, it ensures that the boundary between the "common" and the STOCKDISPLAY class. Assuming that the requirements of the application have changed the next day, the form-based user interface is required. To enable this new feature, you only need to construct a new class STOCKFORM as the observer. No matter what happens, the "common" class does not need any modifications. In fact, it doesn't even know such changes. Similarly, if the requirements change requires the "Common" class to retrieve inquiry 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 "common" and do not pay attention to any changes.

Physics model

As most solutions, problems are on detail. The observer model 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 precisely, the observer registration body indicates its intention to observe. 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 is revoked from the 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. Observer registration

Figure 2 depicts the 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 stored directly in an instance variable, but 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. Keep this, the next operation in our sequence is to store observer references (indicated by calling the add method on the container).

Figure 3. Observer notification

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 invokes the "Notification" method to notify the observer to change.

Figure 4. Observer withdrawn registration

Figure 4 shows the revoked 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 "Remove" method 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 into the "common" instance and passes itself as a parameter to the Register method. "Common" instance (in the container) Save references to the StockDisplay instance. When the inquiry attribute changes, the "Common" instance is notified to the StockDisplay notification by calling the Notification method. When the application is shut down, the StockdisPlay instance uses the following method to undo the "Common" instance: call the Unregister method, 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 real-time graphics that inquiry changes within the transaction date. To do this, we created a new class called StockGraph, which draws a picture of the inquiry (Y-axis) and the day (x-axis). When the application is started, it registers instances of the StockDisplay and the StockGraph class in the "common" instance. Because the body stores the observer in the container (opposite to instance variables), this does not have problems. When the inquiry changes, the "Common" instance notifies the occurrence of the occurrence of the occurrence of the two observer instances in its 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 this is not a requirement, many frames provide a group of interfaces to be implemented to the observers and entities. As shown in the following C # code example, the IOBSERVER interface discloses a public method "notification". 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 the observer mode.

IOBSERVER and IOBSERVABLE interface (C #)

// Interface The All Observer Classes Should Implement

Public interface IOBSERVER {

Void Notify (Object AnObject);

}

// Interface That All Observable Classes Should IMplement

Public interface IOBSERVABLE {

Void Register (IOBSERVER ANOBSERVER);

Void Unregister (IOBSERVER ANOBSERVER);

}

Go back to our sample application, we know the "common" class 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 this interface (rather than by specific classes), the "common" 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 extended. This base class extension reduces 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 such a base class named OBSERVABLEIMPL. Although this task can be done any container, the class will give the observer storage to the hash 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 "Notification" method is called for the observer instance.

ObservableImpl class (C #)

// Helper Class That Implements Observable Interface

Public Class ObservableImpl: IOBSERVABLE

{

Protected hashtable _observercontainer = new hashtable ();

Public void register (IOBSERVER ANOBSERVER)

{

_observercontainer.add (anobserver, anobserver);

}

Public void unregister (IOBSERVER ANOBSERVER)

{

_observercontainer.remove (anobserver);

}

Public void NotifyObservers (Object Anobject)

{

Foreach (IOBSERVER ANOBSERVER IN _OBSERVERCONTAINER.KEYS)

{

AnobServer.notify; ANOBJECT

}

}

}

Our sample application uses this method to use this base class infrastructure: Modify the Common Class class to extend the OBServableImpl class instead of 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 the loose coupling relationship between the classes involved, the multiple main body is repeatedly used in the same function.

An observer example written in C #. The following .NET observer example focuses on the use of IOBSERVABLE and IOBSERVER interfaces and the OBSERVABASE class in our stock applications. In addition to the "common" and STOCKDISPLAY classes, this example uses MainClass to associate the observer and body instance and modify the AskPrice property of the "Common" 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 ApplicationPublic Class stock: ObservableImpl

{

// Instance Variable for ask Price

Object_askprice;

Public Object Askprice

{

Set {_ASKPRICE = VALUE;

Base.notifyObserve (_ASKPRice);

}

}

}

Public Class StockDisplay: IOBSERVER

{

Public void notify (Object Anobject)

{

Console.writeline ("The New Ask Price); AnObject);

}

}

Public class mainclass

{

Public static void main ()

{

Stockdisplay stockdisplay = new stockdisplay ();

Stock stock = new stock ();

Stock.register (stockdisplay);

// Loop 100 Times and Modify the ask Price

For (int Looper = 0; Looper <100; Looper )

{

stock.askprice = looper;

}

Stock.unregister (stockdisplay);

}

}

Observers in the .NET framework

Based on our understanding of the observer model, let us turn attention to this mode in the .NET framework. People who are very familiar with FCLs will notice that there are no IOBSERVER, IOBSERVABLE, or OBSERVABLEIMPL types in the frame. The main reason for these types is that the CLR will discard these types after a period of time. Although you can use these constructs in the .NET application, the introduction of delegation and events can provide new, powerful methods to implement the observer mode, without having to develop specific types dedicated to supporting this mode. In fact, because delegates and events are a CLR level member, the basic structure of this mode is added to the core of the .NET framework. Therefore, the FCL uses the observer mode in its structure.

Introducing a lot of articles in the internal working methods of delegates and events, we are here to tell. We only need to explain the delegation is the equivalent effect of the function pointer object-oriented (and type security). Delegation 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 help the status of the object of the object that is disclosed during runtime. The event indicates that we are used to implement the registration of the observer mode, withdraw the registration and notification method (CLR and a variety of different compilers support it). The delegation is registered in a specific event at runtime. When the event is triggered, all registered delegations will be called to enable them to receive the notification of the event. For a more in-depth introduction to the delegation and event, see An Introduction To Delegates.

Before introducing delegate and events, it is important to note that the various languages ​​supported by CLR can be freely public delegation and event mechanisms, as long as language designers are appropriate. Therefore, these functions cannot be studied in different languages. In order to facilitate the discussion below, we focus on the C # implementation of these features. If you are using the language is not C #, see related documents to learn how to support delegates and events in your language.

According to the term defined by the observer mode, the class of statements is the subject. 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 involves slightly more, but flexibility has improved significantly (we will discuss later). The observer does not implement the IOBSERVER interface and register itself into the body, but must create a specific delegation instance and register this delegate into the main event. Observers must use delegation instances with the type of event declaration, otherwise, registration will fail. During the creation of this delegate instance, the observer will pass the method (instance or static) name of the body to the delegated notice. After the delegate is bound to the method, it can be registered in the event. Similarly, you can cancel this delegation from the event. The body provides a notification to the observer by calling an event. If you are not familiar with the delegates and events, implement the observer mode seems to be a lot of work, especially compared to the IOBSERVABLE interface we used to be used. However, it is easier than sounding, and it is easy to achieve much. The following C # code example focuses on the class modifications required to support delegates and events in our sample application. Note that no "common" or STOCKDISPLAY class is used to support any base class or interface of this mode.

Observers who use delegates and events (C #)

Public Class Stock

{

Public delegate void askPRICEDELEGATE (Object Aprice);

Public Event askPRICEDELEGATE askpricechanged;

// Instance Variable for ask Price

Object_askprice;

// Property for ask Price

Public Object Askprice

{

SET {

_ASKPRICE = VALUE;

AskPRICECHANGED (_ASKPRice);

}

}

}

Public Class stockdisplay, PUBLIC CLASS StockDisplay

{

Public void askPRICECHANGED (Object Aprice)

{

Console.write ("The New Ask Price IS: Aprice " / R / N ");

}

}

Public class mainclass

{

Public static void main ()

{

Stockdisplay stockdisplay = new stockdisplay ();

Stock stock = new stock ();

Stock.askpriceDelegate adlegate = new

Stock.askpricedelegate (stockdisplay.askpricechange);

stock.askpricechanged = adlevation;

For (int Looper = 0; Looper <100; Looper )

{

stock.askprice = looper;

}

stock.askpricechanged- = adlevation;

}

}

After familiar with the delegates and events, you will clearly see their huge potential. Different from IOBSERVERVERVABSERVABLE interfaces and OBSERVABLEIMPL classes, use delegation and events can greatly reduce the work 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 maximum advantage of delegated 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. While using IOBSERVER and IOBSERVABLE interfaces reduce the coupling relationship between the observer and the subject class, the use delegation can completely eliminate these coupling relationships. Event mode

Based on event and delegation, 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 referred to as an event mode. Typically, this mode is expressed as a formal naming convention for delegates, events, 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 delegates to use this mode (the protection mode police!), But Microsoft suggests this.

The first conventions may also be the most important agreement that the name of the event disclosed. This name should be clearly understood for the state change 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 reliable example, let us analyze this agreement on the "common" 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 "Common" class is _askprice, reasonable event name should be askpricechanged. Obviously, the name of this event is more illustrative 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 delegation and its signature. Delegation names should include event names (through the first agreement) and additional words "handler". This mode requires delegate 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 fact that the following facts may be delegated to any potential method 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 delegated return type depends on your implementation, most implementation of the delegate of this mode does not return any values.

Need to pay a little attention to the second parameter e delegate. 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 "common" class, this agreed requires the delegation of the AskPriceChanged event to be named askpricechangedhandler. In addition, the second parameter of this delegate should be named askpricechangedendeventargs. Because we need to pass new inquiry to the observer, we need to extend the System.EventArgs class to name the 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 "common" 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 # and Visual Basic .NET code example displays a complete view of the event mode applied to the "Common" 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;}}

}

in conclusion

Based on this analysis of the observer mode, we can clearly see this model provides a perfect mechanism to ensure clear boundaries between objects in the application, regardless of their role (UI or other) What is it. Although the implementation of the callback (using IOBSERVER and IOBSERVABLE interface) is quite simple, the delegation and event concepts of CLR can handle most "heavy work" and reduce the coupling level between the main body and the observer. In fact, by correctly use this mode, it will take a big step forward in ensuring that the application can evolve. When your UI and business requirements change over time, the observer mode ensures that you can simplify your work. In development flexible applications, the design mode is a very powerful tool (if effectively applied). Writing this article is to illustrate the validity of the mode method and focus on a mode used in the .NET framework. The future article will continue to explore the mode in the FCL and briefly introduce some modes to generate a valid web service. Until then……

Although it looks only to only a few steps, the truly process is often more complicated. The transmission of the message is usually to pass through the intermediate layer (eg, a cache, filter firewall, and credential management system). These intermediate layers can be said to listen to messages to perform partial processing or complete processing. The intermediate layer may produce an abnormality (eg, an authorized intermediate layer that rejects access requests, resulting in returning a sender an error.

Listening is the conceptual basis for the infrastructure of the message. By listening, the message can be routed along a complex path to the shared dedicated service can participate in the message processing.

A more common application is to perform request conversion between services in different messages formats, as shown in Figure 1. This mode can be used to provide a standard-compliant interface in front of the system using a proprietary protocol, and can be used to convert a message that complies with the over-time version of the service agreement.

The request can also be redirected to other service ports based on any element or attribute of the message. This example of this content-based route includes state division (e.g., pressing newslets by date) and network topology optimization (route request to the local service port, rather than the service port of many hop).

The design value of asynchronous messaging has been simply discussed in the service article. Unless we must make the request to receive a response very quickly (for example, an impatient user access to the web request), the service should use a pair of port for asynchronous interaction. A request for a request should provide a response to the port identifier that can be sent in the message header. In fact, the requester is placed at the end of the message processing chain, using the response created by the intermediate layer and service of the basic business logic that implements the request.

转载请注明原文地址:https://www.9cbs.com/read-57996.html

New Post(0)