An article about OBSERVER mode on MSDN

xiaoxiao2021-03-06  18

After seeing, OBServer is now used in the separation of View and Model, and can also be used to separate main features and other accessibility. Can Decorator still solve the problems in the article?

Show TOC

Welcome to MSDN>

Architecture>

Enterprise solutions to Microsoft .NET>

Chapter 3 Web Representation Mode

Implement Observer in .NET

Release Date: 4/1/2004

| Update Date: 4/16/2004

Enterprise Solution Mode for Microsoft .NET> Web Representation> Observer in .NET

Version: 1.0.1

This page

Context background information Implementation policy test Considering results context related mode acknowledgment

Context

You want to build an application in Microsoft? .NET and you must notify the dependence object in the case where the source object does not depend on the dependency object.

Back to top

Background Information

To explain how to implement OBSERVER (viewer) in .NET, and explain the benefits of dependence between restrictions, the following example refers to a solution with two-way dependencies. First, the solution is refactored to the implementation of the OBServer mode defined in Design Patterns [gamma95]; then, the solution is reconstructed into the modification form of the OBServer mode using a language that implements a single inheritance; Reconstructs solutions to delegation and event constructs using the .NET Framework language.

This example problem has two classes, Album, and BillingService (see Figure 1).

Figure 1 UML static illustration

These two objects show the album content by interacting, and charges the end user when the album content is displayed.

Album.cs

The following example shows the implementation of the Album class:

Using system;

Public Class Album

{

Private billingService billing;

PRIVATE STRING NAME;

Public Album (BillingService Billing,

String name)

{

THIS.BILLING = BILLING;

THIS.NAME = Name;

}

Public void play ()

{

Billing.generatecharge (this);

// Code to Play the album

}

Public String Name

{

Get {return name;}

}

}

BillingService.cs

The following example shows the implementation of the BillingService class:

Using system;

Public Class BillingService

{

Public void generatecharge (album album)

{

String name = album.name;

// Code to Generate Charge for Correct Album

}

}

These classes must be created in a specific order. Because the BillingService object is required when constructing the Album class, the former must be constructed after the latter. After the object is instantiated, it will charge the user each time you call a Play method.

Client.cs

The following client class demonstrates the construction process:

Using system;

Class Client

{

[Stathread]

Static void main (string [] args)

{

BillingService Service = new billingService (); album album = new album (service, "up");

Album.Play ();

}

}

This code is operating normally, but at least two problems. The first problem is two-way dependence between the Album class and the BillingService class. Album calls the BillingService method, and BillingService also calls Album methods. This means that if you need to reuse Album classes elsewhere, you must also include BillingService. Similarly, you cannot use the BillingService class without Album classes. This situation is not ideal because it limits flexibility.

The second question is that you must modify the Album class each time you add or delete new services. For example, if you want to add a counter service for tracking the number of photo albums, you must modify the constructor and Play method of the Album class as follows:

Using system;

Public Class Album

{

Private billingService billing;

Private counterservice counter;

PRIVATE STRING NAME;

Public Album (BillingService Billing,

CounterService Counter,

String name)

{

THIS.BILLING = BILLING;

THIS.COUNTER = COUNTER;

THIS.NAME = Name;

}

Public void play ()

{

Billing.generatecharge (this);

Counter.Increment (this);

// Code to Play the album

}

Public String Name

{

Get {return name;}

}

}

This approach is very bad. Obviously, this type of change should not be involved in the Album class. This design makes the code difficult to maintain. However, you can use the OBServer mode to solve these problems.

Back to top

Implementation strategy

This policy discusses and implements many methods for the issues described in the previous part. Each solution is attempted to correct the issues in the previous example by canceling the bidirectional depend between Album and BillingService. The first solution describes how to implement OBServer mode by using the solution described in Design Patterns [gamma95].

Observation

The Design Patterns method uses an abstract Subject class and the OBServer interface to cancel the dependence between the Subject object and the OBServer object. It also takes into account a Subject to have multiple OBServer. In this example, the AlBum class is inherited from the Subject class, thus the role of the specific body described in the OBServer mode. The BillingService class replaces the specific observer by implementing the OBServer interface, because BillingService is waiting for the notification when the Play method is called. (See Figure 2.)

Figure 2 OBServer class diagram

By expanding the Subject class, the direct dependence of the Album class to BillingService can be canceled. However, you now have dependence on the OBServer interface. Because OBServer is an interface, the system does not rely on the actual instance of the implementation interface. Therefore, it is easy to expand without modifying the Album class. You still don't cancel the dependence between BillingService and Album. This cannot be considered a big problem, because you can easily add new services without having to change Album. The following example shows the implementation code of this solution. Observer.cs

The following example shows the OBServer class:

Using system;

Public Interface Observer

{

Void Update (Object Subject);

}

SUBJECT.CS

The following example shows the Subject class:

Using system;

Using system.collections;

Public Abstract Class SUBJECT

{

Private arraylist observers = new arraylist ();

Public Void AddobServer (Observer Observer)

{

Observers.Add (Observer);

}

Public Void RemoveobServer (Observer Observer)

{

Observers.Remove (OBServer);

}

Public void notify ()

{

Foreach (Observer Observer In Observers)

{

Observer.Update (this);

}

}

}

Album.cs

The following example shows the Album class:

Using system;

Public Class Album: Subject

{

PRIVATE STRING NAME;

Public album (String name)

{this.name = name;}

Public void play ()

{

NOTIFY ();

// Code to Play the album

}

Public String Name

{

Get {return name;}

}

}

BillingService.cs

The following example shows the BillingService class:

Using system;

Public Class BillingService: Observer

{

Public Void Update (Object Subject)

{

Subject is album

Generatecharge ((album) subject;

}

Private void generatecharge (album album)

{

String name = album.name;

// Code to Generate Charge for Correct Album

}

}

You can verify that the Album class is no longer dependent on the BillingService class in this example. This is ideal if you need to use Album classes in other contexts. In the example of "background information", if you want to use Album, you need to include the BillingService class.

Client.cs

The following code describes how to create a variety of objects and the order of creating objects. The maximum difference between this constructor and the "background information" example is how the Album class gets the information about BillingService. In the "background information" example, BillingService is explicitly passed to Album as constructor. In this example, a function called AddObServer is called to add BillingService that implements the OBSERVER interface. Using system;

Class Client

{

[Stathread]

Static void main (string [] args)

{

BillingService Billing = new billingService ();

Album album = new album ("UP");

Album.addobserver (billing);

Album.Play ();

}

}

As you can see, the Album class does not quote the billing service. All work it must do is to inherit the Subject class. The Client class passes the reference to the BillingService instance to the album, but the language runtime automatically converts the BillingService reference to the reference to the OBServer interface. The AddobServer method (implemented in the Subject Base class) only processes a reference to the OBSERVER interface; it does not reference billing services. Therefore, this cancels the dependence of any content related to billing services. However, this still has many problems:

Use inheritance to share the Subject implementation. Microsoft Visual Basic? NET development system and C # language allow for multiple inheritance of single inheritance and interfaces. In this example, you need to use a single inheritance to share the Subject implementation. This cannot be used in inherited hierarchies.

Single Observer. At any time, as long as the Play method is called, the Album class will notify the viewer. If you have another function (for example, Cancel), you must send the event to the service with the Album object, so that the service can know this is a Play event or a Cancel event. This makes the service very complicated because the service will receive an event that they may not be interested.

Reduced explicit extent and improve complexity. Direct dependence is now canceled, but the explicit extent of code is lowered. In the original implementation, Album and BillingService have direct dependence, so it can easily know how to call the GenerateChargeCharge method. In this example, Album calls the Notify method in the Subject, which is iterated by a previously registered Observer object, and then call the Update method. In the BillingService class, the Update method will call GenerateCharge. If you want to learn more about explicitly, see Martin Fowler in the article "To BE EXPLICI" in IEEE Software [Fowler01].

Modified OBServer

The main disadvantage of OBServer [gamma95] is that inherits is used as a method of sharing the Subject implementation. In addition, this cannot be explicitly known that OBServer is interested in what activities received. In order to solve these problems, the next portion of this example introduces a modified OBServer. In this solution, you change the SUBJECT class to an interface. You also introduced another class named SubjectHelper, which implemented the Subject interface (see Figure 3).

Figure 3 Modified Observe class diagram

The Album class contains SubjectHelper and is disclosed as a common attribute. This allows a class such as BillingService to access specific SubjectHelper and indicates that if the Album class changes it wants to get notifications. This implementation also allows Album classes to have more than one SubjectHelper; maybe each public activity has one. The following code implements this solution (here the Observer interface and the BillingService class are omitted because they have not changed).

SUBJECT.CS

In the example below, Notify has changed because you must now pass the Subject to the SubjectHelper class. This is unnecessary in the OBServer [gamma95] example because the Subject class is the base class.

Using system;

Using system.collections;

Public Interface Subject

{

Void AddobServer (OBServer Observer);

Void RemoveobServer (Observer Observer);

Void Notify (Object RealSubject);

}

SubjectHelper.cs

The following example shows the newly created SubjectHelper class:

Using system;

Using system.collections;

Public Class SubjectHelper: SUBJECT

{

Private arraylist observers = new arraylist ();

Public Void AddobServer (Observer Observer)

{

Observers.Add (Observer);

}

Public Void RemoveobServer (Observer Observer)

{

Observers.Remove (OBServer);

}

Public void Notify (Object RealSubject)

{

Foreach (Observer Observer In Observers)

{

Observer.Update (RealSubject);

}

}

}

Album.cs

The following example shows that when using the SubjectHelper instead of inheriting the Subject class, the Album class changes:

Using system;

Public Class Album

{

PRIVATE STRING NAME;

Private subject playsubject = new subjecthelper ();

Public album (String name)

{this.name = name;}

Public void play ()

{

PlaysUBject.notify (this);

// Code to Play the album

}

Public String Name

{

Get {return name;}

}

Public Subject Playsusubject

{

Get {returnomesubject;}

}

}

Client.cs

The following example shows what changes have a CLIENT class:

Using system;

Class Client

{

[Stathread]

Static void main (string [] args)

{

BillingService Billing = new billingService ();

Counterservice counter (); album album = new album ("UP");

Album.playSubject.addobserver (billing);

Album.PlaySubject.addobserver (Counter);

Album.Play ();

}

}

Perhaps you can see some of the advantages brought about by decreasing the coupling between classes. For example, although this reconstruction adjusts the implementation of Subject and Album, the BillingService class does not have to change. In addition, the Client class is easier to read because you can specify which specific event to connect the service to.

Obviously, the modified OBServer solution solves the problem of previous solutions. In fact, this is the preferred implementation method for only a single implementation inherited language. However, this solution still has the following shortcomings:

more complicated. The original solution consists of two classes, which are directly cited in an explicit manner, and now there are two interfaces and three classes for indirect dialogue, and also include many of the code not in the first example. . There is no doubt that you start thinking about whether the original dependence is not so bad. However, you should remember that these two interfaces and SubjectHelper classes can be reused by any multiple observations. Therefore, in the entire application, they may only need to write once.

Reduce explicit. This solution is the same as Observer [gamma95], it is difficult to determine which viewer is observing the changes to the Subject.

Therefore, this solution is a good object-oriented design, but you need to create many classes, interfaces, associations, and more. All this is really necessary in .NET? The answer is "NO", please see the example below.

Observer in .NET

Using the .NET's built-in function, you can implement Observer mode with less code. You don't need Subject, SubjectHelper and Observer type, because there is a public language running library, they have been outdated. Delegates and events introduced in .NET allow you to implement Observer without developing specific types.

In the .NET-based implementation, the event represents an abstraction of the SubjectHelper class described in the "modified observer" (supported by the public language runtime and various compilers). Album class public event type instead of SubjectHelper. The observer role is more complicated than before. The observer must create a specific delegation instance and register the delegate to the main event, rather than implementing the OBServer interface and register itself to the body. The observer must use the delegate instance of the type specified by the event statement; otherwise, the registration will fail. During the creation of this delegated instance, the viewer provides a method name (instance or static) that will accept the body notification. When delegated to the method, it can register with the main event. Similarly, you can also log out of this delegation. The body provides a notification to the observer by calling an event. [Purdy02]

The following code example demonstrates the changes made to the examples in the "Modified Observer" in order to use delegates and events.

Album.cs

The following example shows how the Album class open event type:

Using system;

Public Class Album

{

PRIVATE STRING NAME;

Public Delegate void PlayHandler (Object Sender);

Public Event PlayHandler PlayEvent;

Public album (String name)

{this.name = name;} public void play ()

{

NOTIFY ();

// Code to Play the album

}

Private void notify ()

{

IF (PlayEvent! = NULL)

PlayEvent (this);

}

Public String Name

{

Get {return name;}

}

}

BillingService.cs

As shown in the example below, changes to the BillingService class in the example in "Modified Observer" only need to delete the implementation of the OBServer interface:

Using system;

Public Class BillingService

{

Public Void Update (Object Subject)

{

Subject is album

Generatecharge ((album) subject;

}

Private void generatecharge (album thealbum)

{

// Code to Generate Charge for Correct Album

}

}

Client.cs

The following example shows how to modify the Client class to use new events disclosed by the Album class:

Using system;

Class Client

{

[Stathread]

Static void main (string [] args)

{

BillingService Billing = new billingService ();

Album album = new album ("UP");

Album.PlayEvent = new album.playhandler (billing.Update);

Album.Play ();

}

}

As you can see, the structure of the program is very similar to the previous example. The built-in function of .NET replaces the explicit OBServer mechanism. When you are accustomed to the syntax of delegates and events, their use is more intuitive. You don't have to implement the SubjectHelper class described in "Modified Observer" and the Subject and Observer interface. These concepts are implemented directly in the public language runtime library.

The biggest advantage of delegate is that they can reference any method (as long as the method meets the same signature). This allows any classes to act as a viewer, regardless of what interface or inheritance. Use the OBServer and the Subject interface to reduce the coupling between the observer class and the main class, and the delegated use completely eliminates this coupling. For more information on this topic, see "Exploring the Observer Design Pattern" topic in the MSDN® Developer Library [Purdy02].

Back to top

Test consideration

Because delegates and events completely cancel the two-way dependence between Album and BillingService, you can now write these two class test code independently.

Albumfixture.cs

The AlbumFixTure class describes sample unit tests in NUnit (http://www.nunit.org, which verifies whether PlayEvent is triggered when calling the Play method:

Using system;

Using nunit.framework;

[TestFixTure]

Public Class AlbumFixTure

{

PRIVATE BOOL EVENTFIRED;

Private album album;

[Setup]

Public void init ()

{

Album = New Album ("UP");

Eventfired = false;

}

[TEST] public void attach ()

{

Album.PlayEvent = New Album.PlayHandler (onplay); ONPLAY

Album.Play ();

Ask.ASSERTEQUALS (TRUE, EVENTFIRED);

}

[TEST]

Public void donotattach ()

{

Album.Play ();

Ask.ASSERTEQUALS (False, EventFired);

}

Private void onplay (Object Subject)

{

EventFired = true;

}

}

Back to top

Result context

After comprehensive measurement, the advantages of using delegation and event models in .NET implementation Observer have clearly exceeded potential disadvantages.

advantage

Implement Observer in .NET has the following advantages:

Dependence is canceled. The above example clearly shows that the dependence between the Album and the BillingService class has been canceled.

Improved scalability. "Observer in .NET" example illustrates how easy it is to add a new type of viewer. The Album class is an example of "open-closed" principle, which was originally written in the Object-Oriented Software Construction Second Edition [Bertrand00], which describes classes that are easy to expand but do not modify. The Album class reflects this principle because you can add PlayEvent's observer without having to modify the Album class.

Improved taperable. "Test Considering" explains why you can test the Album class without instantiation of BillingService. The test verifies that the Album class can run correctly. The test also provides an excellent example of how to write BillingService.

Disadvantage

As shown in the example, the implementation of the OBServer is simple and straightforward. However, as the number of delegates and events is increasing, it is difficult to track what happened when the event is triggered. Therefore, the code becomes difficult to debug because you must search for the viewer in your code.

Back to top

Related mode

For detailed backgrounds of the concept discussed here, see the following related modes:

Observer

Model-View-Controller

Back to top

Thank you

[Bertrand00] Meyer, Bertrand. Object-Oriented Software Construction, 2nd Edition .pentice-Hall, 2000.

[Fowler01] Fowler, Martin. "IEEE Software, November / DecEmber 2001.

Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2003.

[Gamma95] Gamma, Helm, Johnson, And Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

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

New Post(0)