18. Design mode – observer mode

Time:2021-9-2

Observer mode can be said to be a design mode very close to our life. Why do you say so? There is a philosophical saying that “everything is connected”. The original meaning is that there is no isolated thing in the world, but in fact, it can also be understood that the occurrence of any event must be caused by a pre event and another post event. Our life is full of all kinds of interrelated events, and the observer model is mainly a set of solutions to deal with such events.

Example

The observer mode has different implementation methods under different requirements. Let’s take an example and deeply feel how it works through gradual improvement.

In the middle school, there is a text “oral skills”, in which there is a sentence “when you smell the barking of dogs in the deep lane, a woman feels stunned and her husband talks nonsense. When the child wakes up, he cries loudly. ” There should be no translation? The next step is to simulate this scenario through the program.

Let’s take a look at the relationship between them, as shown in the figure below:
18. Design mode - observer mode

First edition implementation

A dog barking triggered a series of events. The demand is very clear and simple. Therefore, we can easily achieve the following implementation:

public class Wife
{
    public void Wakeup()
    {
        Console. Writeline ("a woman is surprised to stretch");
    }
}

public class Husband
{
    public void DreamTalk()
    {
        Console.writeline ("his husband's nonsense");
    }
}

public class Son
{
    public void Wakeup()
    {
        Console. Writeline ("wake up and cry");
    }
}

public class Dog
{
    private readonly Wife _wife = new Wife();
    private readonly Husband _husband = new Husband();
    private readonly Son _son = new Son();
    public void Bark()
    {
        Console. Writeline ("remote sensing of dog barking in deep lane");

        _wife.Wakeup();
        _husband.DreamTalk();
        _son.Wakeup();
    }
}

The function is realized. The call is very simple, so there is no code. FromDogIt can be seen from the class that it is the barking of a dog that triggers a series of subsequent events. However, people with certain experience will soon find that it violates at least the opening and closing principle and the Dimitri principle, which will eventually lead to troublesome expansion and maintenance. Therefore, it needs to be improved, and the improved method is not difficult to think of. It is nothing more than abstracting a base class or interface to turn the part of implementation oriented programming into abstract oriented programming, and the real key is what is abstract. Is it abstracting a base class and lettingWife,Husband,SonInherit from this base class? They are all family members, which seems feasible, but they have no public implementation, and what if they join cats, mice or something later? It will become more out of touch. In the face of this unknown change, it is obviously difficult to abstract a public base class, and it may be more appropriate to abstract an interface for the behavior of “observing the occurrence of events”.

Evolution I – simple observer model

According to this idea, let’s take a look at the improved implementation. First define a public interface:

public interface IObserver
{
    void Update();
}

Here is a definition that has nothing to do with any subclassvoid Update()Method, this is also the way we can’t help, because we can’t directlyWakeup()perhapsDreamTalk()Method can only standardize a public behavior interface in this way, which means that when the observed event occurs, some states of the specific instance are updated. The specific implementation class is simple:

public class Wife : IObserver
{
    public void Update()
    {
        Wakeup();
    }

    public void Wakeup()
    {
        Console. Writeline ("a woman is surprised to stretch");
    }
}

public class Husband: IObserver
{
    public void DreamTalk()
    {
        Console.writeline ("his husband's nonsense");
    }

    public void Update()
    {
        DreamTalk();
    }
}

public class Son : IObserver
{
    public void Update()
    {
        Wakeup();
    }

    public void Wakeup()
    {
        Console. Writeline ("wake up and cry");
    }
}

hereUpdate()It is only equivalent to forwarding once. Of course, you can also add your own logic. The big change isDogClass, but they are also the common methods used in the previous combination mode and sharing mode, as shown below:

public class Dog
{
    private readonly IList<IObserver> _observers = new List<IObserver>();

    public void AddObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void Bark()
    {
        Console. Writeline ("remote sensing of dog barking in deep lane");
        foreach (var observer in _observers)
        {
            observer.Update();
        }
    }
}

Not difficult to understand, becauseWife,Husband,SonIt’s all doneIObserverInterface, so you canIList<IObserver>Collection, andAddObserver(IObserver observer)andRemoveObserver(IObserver observer)Add and delete specific instances.

Let’s look at the calling code:

static void Main(string[] args)
{
    Dog dog = new Dog();
    Wife wife = new Wife();
    Husband husband = new Husband();
    Son son = new Son();
    dog.AddObserver(wife);
    dog.AddObserver(husband);
    dog.AddObserver(son);
    dog.Bark();
    Console.WriteLine("----------------------");
    dog.RemoveObserver(son);
    dog.Bark();
}

In fact, this is the simplest observer model of requirements, in whichDogIs the observed, that is, the observed subject, andWife,Husband,SonAre observers. Let’s take a look at its class diagram:
18. Design mode - observer mode

From this class diagram, we may find a problem. Since the observer implements an abstract interface, the observer should also implement an abstract interface. After all, interface oriented programming! Yes, but should the implementation interface or inherit the abstract class? Let’s put it on hold for the time being and superimpose a requirement first.

Evolution II

Turning over the textbook, you can see, “when you hear the barking of dogs in the deep lane, a woman feels surprised and stretched out, and her husband talks nonsense. When the child wakes up, he cries loudly, There are three words “husband also wakes up.”( There are many more in the future. In order to avoid being too complicated, we won’t consider them). Let’s take a look at the relationship between them:
18. Design mode - observer mode

It can be seen from the context that the husband was awakened by the cry of his son, not the barking of a dog. Based on these, we can analyze the following three points:

  1. There are two observers, one is a dog and the other is a son;
  2. The husband observed two things, one was the dog barking, the other was the son crying;
  3. The son is both an observer and an observer.

It feels a lot more complicated at once, but fortunately, with the foreshadowing in front, it doesn’t seem to be particularly difficult to realize,WifeandDogThere is no change. What needs to be modified isHusbandandSon, the code is as follows:

public class Husband : IObserver
{
    public void DreamTalk()
    {
        Console.writeline ("his husband's nonsense");
    }

    public void Update()
    {
        DreamTalk();
    }

    public void Wakeup()
    {
        Console.writeline ("Fu Yixing");
    }
}

public class Son : IObserver
{
    private readonly IList<IObserver> _observers = new List<IObserver>();

    public void AddObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void Update()
    {
        Wakeup();
    }

    public void Wakeup()
    {
        Console. Writeline ("wake up and cry");
        foreach (var observer in _observers)
        {
            observer.Update();
        }
    }
}

As you can see,HusbandOne moreWakeup()method,SonThe logic of observer and observed is realized at the same time.

Of course, there are some changes in the place of calling, after allSonThe status of is different, and the code is as follows:

static void Main(string[] args)
{
    Dog dog = new Dog();
    Wife wife = new Wife();
    Husband husband = new Husband();
    Son son = new Son();
    dog.AddObserver(wife);
    dog.AddObserver(husband);
    dog.AddObserver(son);
    son.AddObserver(husband);
    dog.Bark();
}

Seeing this, careful people will find that there are many problems in this code, at least the following two points:

  1. DogandSonThere are a lot of duplicate codes in the;
  2. Run it and you’ll findHusbandThe function of was not realized becauseHusbandThere is no identification of the type or source of the event, so you don’t know whether to talk in your sleep or wake up.

Evolution III – standard observer model

In order to solve the above two problems, we need to make another improvement. First, the problem of code duplication can be solved by extracting a common base class, while the second problem must be distinguished by passing parameters. We can first define a class carrying event parameters, Event parameters usually contain at least event source and event type (of course, other attributes). The code is as follows:

public class EventData
{
    public object Source { get; set; }

    public string EventType { get; set; }
}

The modified observer interface and the extracted observer base class are as follows:

public interface IObserver
{
    void Update(EventData eventData);
}

public class Subject
{
    private readonly IList<IObserver> _observers = new List<IObserver>();

    public void AddObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void Publish(EventData eventData)
    {
        foreach (var observer in _observers)
        {
            observer.Update(eventData);
        }
    }
}

As you can see, the observerIObserverEvent parameters are added to the observerSubjectNeither interfaces nor abstract classes are used. In principle, this is inappropriate. However, there are no abstract methods in this class and it is not suitable to use abstract classes. Therefore, we can only reluctantly use ordinary classes.

Other codes are as follows:

public class Wife : IObserver
{
    public void Update(EventData eventData)
    {
        if (eventData.EventType == "DogBark")
        {
            Wakeup();
        }
    }

    public void Wakeup()
    {
        Console. Writeline ("a woman is surprised to stretch");
    }
}

public class Husband : IObserver
{
    public void DreamTalk()
    {
        Console.writeline ("his husband's nonsense");
    }

    public void Update(EventData eventData)
    {
        if (eventData.EventType == "DogBark")
        {
            DreamTalk();
        }
        else if (eventData.EventType == "SonCry")
        {
            Wakeup();
        }
    }

    public void Wakeup()
    {
        Console.writeline ("Fu Yixing");
    }
}

public class Son : Subject, IObserver
{
    public void Update(EventData eventData)
    {
        if (eventData.EventType == "DogBark")
        {
            Wakeup();
        }
    }

    public void Wakeup()
    {
        Console. Writeline ("wake up and cry");
        Publish(new EventData { Source = this, EventType = "SonCry" });
    }
}

public class Dog : Subject
{
    public void Bark()
    {
        Console. Writeline ("remote sensing of dog barking in deep lane");

        Publish(new EventData { Source = this, EventType = "DogBark" });
    }
}

It can be seen that the observed passes throughPublish(EventData eventData)Method sends an event, and the observer determines what action to perform next through the event type in the parameter. The following is its class diagram:
18. Design mode - observer mode
This is actually the observer pattern defined by GOF.

definition

There is a one to many dependency between multiple objects. When the state of an object changes, all objects that depend on it are notified and updated automatically.

UML class diagram

Simplify the class diagram of the above example to obtain the following class diagram of observer mode:
18. Design mode - observer mode

  • SubjectAbstract topic role, which is an abstract class (but actually I use a common class), provides a method for saving the collection of observer objects and adding, deleting and notifying all observers.
  • ConcreteSubject: specific subject roles.
  • IObserverAbstract observer role, which is an interface that provides a method to update itself and is called when notified of changes to a specific topic.
  • Concrete Observer: concrete observer role, which implements the interface defined in the abstract observer to update its own state when notified of the change of the topic.

Advantages and disadvantages

advantage

  1. It reduces the coupling relationship between the subject and the observer;
  2. A trigger mechanism is established between the subject and the observer.

shortcoming

  1. The dependency between the subject and the observer is not completely removed, and circular references may occur;
  2. When there are many observers, event notification will take a lot of time and affect the efficiency of the program.

Of course, the shortcomings here refer to the shortcomings of the observer model. In fact, there will be more shortcomings in the above examples. We will try to solve them later.

Notification mode

In fact, there are two modes of event notification in the observer mode-Push modeandPull mode, here is a brief explanation. The above implementation uses the push mode, that is, the topic actively pushes the event message to the observer. The advantage is real-time and efficient, which is also a recommended method.

However, the push mode is not suitable for all scenes. For example, a topic has many observers, but each observer only focuses on one or some states of the topic. At this time, the push mode is not suitable, because the push mode will push all States of the topic to all observers without distinction. For observers, The news is too cumbersome. At this time, the pull mode can be adopted. The topic discloses all the observable states, and the observer actively pulls the part of interest.

The pull mode can be implemented in two ways according to different situations. One way is for the observer to check regularly and pull the data. This operation is simple and rough, but it will cause a large performance burden to the subject. At the same time, it will also bring varying degrees of delay due to different inspection frequencies. The other way is that the subject takes the initiative to send a notification, but the notification does not take any parameters. It just tells the observer that the subject has changed, and then the observer pulls the part he pays attention to. This is the most commonly used method in the pull mode.

summary

Well, the analysis of the observer mode defined by GOF is finished, but in fact, the observer mode is far from over. Limited to space, we will continue to analyze it in the next article. However, before that, you can think about the following two questions in advance:

  1. dog.AddObserver(...)Is it really appropriate? In real life, do dogs really have this ability?
  2. We knowC#Multiple inheritance is not supported in, ifDogItself inherited fromAnimalHow can the base class of be implemented as an observer in addition to the implementation of the above evolution one? Because this kind of scene is too common.

Only by thinking clearly about these two problems can the observer model really show its power.

Source code link

Recommended Today

STM32 network circuit design

In previous tweets《SMI interface of STM32 network》《MII and RMII interfaces of STM32 network》, all interfaces of STM32 Ethernet and external PHY are introduced. If some students are not familiar with SMI, MII and RMII interfaces, it is recommended to read the two articles mentioned above, otherwise they may not understand the following. ​ Zone 1:We […]