From simple to deep, big talk design mode -- observer mode

Posted by kristolklp on Tue, 08 Mar 2022 06:21:28 +0100

The observer pattern defines a one to many dependency that allows multiple observer objects to listen to a subject object at the same time. When the state of the subject object changes, it will notify all observer objects so that they can update themselves automatically. -- Dahua design mode

How would you write the code if you had to inform others of the meeting?

Basic operation:

public class ObserverPattern : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        MySelf mySelf = new MySelf();
        WorkerMate mate1 = new WorkerMate("Zhang San", mySelf);
        WorkerMate mate2 = new WorkerMate("Li Si", mySelf);

        mySelf.Add(mate1);
        mySelf.Add(mate2);
        mySelf.Action = "There's a meeting!";

        mySelf.Notify();
    }
}

class MySelf
{
    private List<WorkerMate> mates = new List<WorkerMate>();
    private string action;

    public void Add(WorkerMate mate)
	{
        mates.Add(mate);
    }

    public void Notify()
	{
		for (int i = 0; i < mates.Count; i++)
            mates[i].Update();
	}

	public string Action { get; set; }
}

//colleague
class WorkerMate
{
    private string name;
    private MySelf mySelf;

	public WorkerMate(string name, MySelf mySelf)
	{
		this.name = name;
		this.mySelf = mySelf;
	}

    public void Update()
	{
        Debug.Log($"{mySelf.Action}{name},Go to the meeting.");
    }
}

This is done, but there will be a problem. The coupling is particularly serious, and what if colleagues don't want to go to the meeting? In this way, an additional class will be added and the original code will be modified.

Advanced version:

We add an abstract class to our colleagues so that we can handle different events and don't forget our notifier.

//Notifier
interface Subject
{
    void Add(Observer observer);
    void Notify();
    string Action { get; set; }
}
//own
class MySelf: Subject
{
    private List<Observer> observers = new List<Observer>();
    private string action;

    public void Add(Observer observer)
	{
        observers.Add(observer);
    }

    public void Notify()
	{
		for (int i = 0; i < observers.Count; i++)
            observers[i].Update();
	}

	public string Action { get => action; set => action = value; }
}
//Observer
abstract class Observer
{
    protected string name;
    protected Subject subject;

    public Observer(string name, Subject subject)
    {
        this.name = name;
        this.subject = subject;
    }

    public abstract void Update();

}
//Colleague A
class WorkerMateA: Observer
{
	public WorkerMateA(string name, Subject subject) :base(name, subject)
	{
	}

	public override void Update()
	{
        Debug.Log($"{subject.Action}{name},Go to the meeting.");
    }
}

//Colleague B
class WorkerMateB : Observer
{
    public WorkerMateB(string name, Subject subject) : base(name, subject)
    {
    }

    public override void Update()
    {
        Debug.Log($"{subject.Action}{name},I don't want to go to the meeting.");
    }
}
//Main function 
void Start()
    {
        MySelf mySelf = new MySelf();
        WorkerMateA mate1 = new WorkerMateA("Zhang San", mySelf);
        WorkerMateB mate2 = new WorkerMateB("Li Si", mySelf);

        mySelf.Add(mate1);
        mySelf.Add(mate2);
        mySelf.Action = "There's a meeting!";

        mySelf.Notify();
    }

In this way, it will be convenient for observers whether they want to have a meeting or change their personal notice, but the decoupling problem has not been solved. Let's solve it now.

Professional Edition:

Here we use the delegate, which adds the corresponding observer event and notifies the observer to call. Here, all observers inherit an interface in a unified way (for management convenience, you can also inherit it).

//Observer
abstract class Observer
{
    protected string name;
    protected Subject subject;

    public Observer(string name, Subject subject)
    {
        this.name = name;
        this.subject = subject;
    }
}
//Colleague A
class WorkerMateA: Observer
{
	public WorkerMateA(string name, Subject subject) :base(name, subject)
	{
	}

	public void Update1()
	{
        Debug.Log($"{subject.Action}{name},Go to the meeting.");
    }
}

//Colleague B
class WorkerMateB : Observer
{
    public WorkerMateB(string name, Subject subject) : base(name, subject)
    {
    }

    public void Update2()
    {
        Debug.Log($"{subject.Action}{name},I don't want to go to the meeting.");
    }
}
//Notifier
interface Subject
{
    void Notify();
    string Action { get; set; }
}
//own
class MySelf: Subject
{
    public Action Update;
    private string action;

    public void Notify()
	{
        Update();
    }

	public string Action { get => action; set => action = value; }
}
 void Start()
    {
        MySelf mySelf = new MySelf();
        WorkerMateA mate1 = new WorkerMateA("Zhang San", mySelf);
        WorkerMateB mate2 = new WorkerMateB("Li Si", mySelf);

        mySelf.Update += mate1.Update1;
        mySelf.Update += mate2.Update2;
        mySelf.Action = "There's a meeting!";

        mySelf.Notify();
    }

Summary:

 

When the change of one object needs to change other objects at the same time, and we don't know how many to change, we should consider using the observer mode.

The work of observer mode is actually decoupling. Let both sides of the coupling rely on abstraction rather than concrete. So that their changes will not affect the changes on the other side

Topics: C# Design Pattern