Simple implementation of observer pattern in Unity.

The observer pattern will not be introduced here. As the most basic design pattern, the concept is very simple.

I have read a lot of blogs myself, but I always don’t know how to start with the specific implementation. I am also a beginner, so after I implement it myself, I will post it for your reference.

One, train of thought

1. The observer is registered to the observer pool

It is equivalent to putting the goods on the shelf.

2. The observer specifies to observe an observed object

It is equivalent to adding an item to the shopping cart that is already on the shelf.

3. When the observed person sends a discount message, the shopping cart will have a discount reminder.

Second, all the classes used

Observer's fixed class

1. IObserver: observer interface

2, Theobserved: Observer class

3. TheobservedName: The names of all observed objects are in it. After all, there are many observed objects, so there must always be a name basis

4, TheobservedPool: The observed pool, all the logic of adding and removing is carried out in it

Demo project logic class

1, ObserverText, text Text (observer)

2, TheobservedClick, Button (observed)

Third, the expected effect of concrete realization

After clicking the button

Normal process, click button, get object, get<Text>, modify

Observer mode process, the button is registered to the observed pool, the text is specified to observe the button, the button is clicked, and pushed to all the observers who registered to observe themselves, and the observer modifies the text.

Fourth, code implementation

1. Observer code that has nothing to do with project logic

1. IObserver: observer interface

/// <summary>
/// 观察者
/// </summary>
public interface IObserver
{
    void Monitor();
}

2, Theobserved: Observer class

/// <summary>
/// 被观察者
/// </summary>
public class Theobserved : MonoBehaviour
{
    /// <summary>
    /// 观察者列表
    /// </summary>
    private List<IObserver> Observers = new List<IObserver>();
    /// <summary>
    /// 发送消息给观察者
    /// </summary>
    public void SendObservers()
    {
        for (int i = 0; i < Observers.Count; i++)
        {
            Observers[i].Monitor();
        }
    }
    /// <summary>
    /// 添加观察者
    /// </summary>
    /// <param name="observer"></param>
    public void AddObserver(IObserver observer)
    {
        if (!Observers.Contains(observer))
        {
            Observers.Add(observer);
        }
    }
    /// <summary>
    /// 移除观察者
    /// </summary>
    /// <param name="observer"></param>
    public void RemoveObserver(IObserver observer)
    {
        if (Observers.Contains(observer))
        {
            Observers.Remove(observer);
        }
    }
    
}

3. TheobservedName: The name of the observed object

Here I use reflection to automatically assign values ​​​​to the properties inside, and I need to call the method of initializing properties.

One of the properties inside is the Observer when I demonstrated

public class TheobservedNames
{
    /// <summary>
    /// 对该类的属性自动赋值
    /// </summary>
    public static void InitializationAttributes()
    {
        Type type = typeof(TheobservedNames);
        var obj = Activator.CreateInstance(type);//得到该类实例
        PropertyInfo[] finfo = obj.GetType().GetProperties();//取得有属性
        for (int i = 0; i < finfo.Length; i++)
        {
            finfo[i].SetValue(obj, Convert.ChangeType(finfo[i].Name, finfo[i].PropertyType), null);//全部赋值
        }
    }
    /// <summary>
    /// Button点击
    /// </summary>
    public static string TheobservedClick { get; private set; }
}

4, TheobservedPool: The observed pool, all the logic of adding and removing is carried out in it

/// <summary>
/// 被观察者池
/// </summary>
public class TheobservedPool
{
    /// <summary>
    /// 所有被观察者
    /// </summary>
    private static Dictionary<string, Theobserved> Theobserveds = new Dictionary<string, Theobserved>();
    /// <summary>
    /// 添加被观察者
    /// </summary>
    /// <param name="theobservedName"></param>
    /// <param name="theobserved"></param>
    public static void AddTheobserved(string theobservedName, Theobserved theobserved)
    {
        if (!Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds.Add(theobservedName, theobserved);
        }
    }
    /// <summary>
    /// 移除被观察者
    /// </summary>
    /// <param name="theobservedName"></param>
    public static void RemoveTheobserved(string theobservedName)
    {
        if (Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds.Remove(theobservedName);
        }
    }
    /// <summary>
    /// 观察者观察指定对象
    /// </summary>
    /// <param name="theobservedName"></param>
    /// <param name="observer"></param>
    public static void AddObserver(string theobservedName, IObserver observer)
    {
        if (Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds[theobservedName].AddObserver(observer);
        }
        else
        {
            Debug.Log("没有该对象");
        }
    }
    /// <summary>
    /// 观察者退出观察指定对象
    /// </summary>
    /// <param name="theobservedName"></param>
    /// <param name="observer"></param>
    public static void RemoveObserver(string theobservedName, IObserver observer)
    {
        if (Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds[theobservedName].RemoveObserver(observer);
        }
        else
        {
            Debug.Log("没有该对象");
        }
    }
}

Second, this demo observes the implementation code of the observer mode

There are two scripts

ObserverText: Observer script hanging on Text

public class ObserverText : MonoBehaviour, IObserver
{
    public Text Text;
    public void Monitor()
    {
        Text.text = string.Format("{0}{1}",transform.name, "收到了消息");
    }

    void Start()
    {
        //观察TheobservedClick这个被观察者
        TheobservedPool.AddObserver(TheobservedNames.TheobservedClick,this);
    }
}

TheobservedClick: the script hanging on the button

It is necessary to initialize the names of all observers once at the beginning of the project. Normally, it should not be initialized here. I put it here for convenience.

public class TheobservedClick : Theobserved
{

    void Awake () {
        //所有被观察者的名字初始化进行赋值
        TheobservedNames.InitializationAttributes();
        //把自己添加到观察者池
        TheobservedPool.AddTheobserved(TheobservedNames.TheobservedClick,this);
        GetComponent<Button>().onClick.AddListener(()=> 
        {
            SendObservers();
        });
    }
}

Five, matters needing attention

It can be seen carefully that I register the observed in Awake, and register the observer in Start. Here, we should pay attention to the problem of calling order. If the observer registers too early, he will not be able to register.

Because there is no guarantee that the registration of the observer object must be in such a sequence after the registration of the observed object.

So we need to add one. If the observer registers first, and then the observer registers, I need to put the observer registered in advance in the observation list of the observer registered later.

6. Early registration of observers

The previous code has not been changed, mainly adding a few lines of code

In the TheobservedPool script, add a collection of observer objects registered in advance, and then add a few lines of code in the two methods

    /// <summary>
    /// 提前注册的观察者对象
    /// </summary>
    private static Dictionary<string, List<IObserver>> FrontObserver = new Dictionary<string, List<IObserver>>();
    /// <summary>
    /// 添加被观察者
    /// </summary>
    /// <param name="theobservedName"></param>
    /// <param name="theobserved"></param>
    public static void AddTheobserved(string theobservedName, Theobserved theobserved)
    {
        if (!Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds.Add(theobservedName, theobserved);
            //把提前注册的观察者,给添加进来
            if (FrontObserver.ContainsKey(theobservedName))
            {
                Theobserveds[theobservedName].UnionObservers(FrontObserver[theobservedName]);
             FrontObserver.Remove(theobservedName);//移除
            }
        }
    }


    /// <summary>
    /// 观察者观察指定对象
    /// </summary>
    /// <param name="theobservedName"></param>
    /// <param name="observer"></param>
    public static void AddObserver(string theobservedName, IObserver observer)
    {
        if (Theobserveds.ContainsKey(theobservedName))
        {
            Theobserveds[theobservedName].AddObserver(observer);
        }
        else
        {
            //此时还没有该被观察者对象,加入等待列表
            if (FrontObserver.ContainsKey(theobservedName))
            {
                FrontObserver[theobservedName].Add(observer);
            }
            else
            {
                FrontObserver.Add(theobservedName, new List<IObserver>() { observer });
            }
        }
    }

Added a new method of merging observer lists in Theobserved, referencing Systeam.Linq

 /// <summary>
    /// 合并观察者列表
    /// </summary>
    /// <param name="FrontObservers"></param>
    public void UnionObservers(List<IObserver> FrontObservers)
    {
        Observers = Observers.Union(FrontObservers).ToList();
    }

Then I changed the wake in logic code A to Start, and changed Start to Awake. The observer registers first, and the observed registers again, and the effect is still the same.

But it should be noted that, to initialize attributes, TheobservedNames.InitializationAttributes(); This method must be executed first

If the product does not even have a name, it cannot be listed.

If it is applied to the project, some methods with parameters need to be added. It is impossible for all methods to have no parameters. Add an overloaded generic method to the observer interface, and add a generic method call to the observer pool. up. The specific code will not be posted.

over.

Guess you like

Origin blog.csdn.net/qq_41886896/article/details/99291404
Recommended