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.