这里就不介绍观察者模式了,作为最基础的设计模式,概念是很简单的。
我自己看过好多博客,但是具体实现起来总是有些不知道怎么下手,我也是初学者,所以自己实现后,晒出来给大家参考。
一,思路
1,被观察者注册到被观察者池
相当于把货物摆上货架。
2,观察者指定观察某个被观察者
相当于把已经在货架上的某件物品加入购物车。
3,被观察者发送打折消息,购物车就会有打折的提示。
二,所有用到的类
观察者的固定类
1,IObserver:观察者接口
2,Theobserved:被观察者类
3,TheobservedName:所有被观察对象名字都在里面,毕竟被观察者很多,总是要有个名字依据的
4,TheobservedPool:被观察者池,所有的添加移除逻辑都在里面进行
Demo的项目逻辑类
1,ObserverText,文本Text(观察者)
2,TheobservedClick,Button(被观察者)
三,具体实现的期望效果
点击button后
正常的流程,点击button,获取对象,get<Text>,修改
观察者模式流程,button注册到被观察者池,text指定观察button,button点击,推送给所有注册观察自己的观察者,观察者修改text。
四,代码实现
一,跟项目逻辑无关的观察者代码
1,IObserver:观察者接口
/// <summary>
/// 观察者
/// </summary>
public interface IObserver
{
void Monitor();
}
2,Theobserved:被观察者类
/// <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:被观察对象名字
这里我用了反射自动为里面的属性赋值,需要调用初始化属性的方法。
里面的一个属性是我演示时的那个被观察者
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:被观察者池,所有的添加移除逻辑都在里面进行
/// <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("没有该对象");
}
}
}
二,本次演示观察者模式实现代码
一共两个脚本
ObserverText:挂在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:挂在button上的脚本
需要项目一开始把所有被观察者的名字初始化一次,正常情况不应该在这里初始化,为了方便我就放在这里了
public class TheobservedClick : Theobserved
{
void Awake () {
//所有被观察者的名字初始化进行赋值
TheobservedNames.InitializationAttributes();
//把自己添加到观察者池
TheobservedPool.AddTheobserved(TheobservedNames.TheobservedClick,this);
GetComponent<Button>().onClick.AddListener(()=>
{
SendObservers();
});
}
}
五,注意事项
细心的可以看出,我在Awake中进行注册被观察者,Start中进行观察者注册,这里要注意的是调用顺序问题,观察者过早注册会注册不上。
因为并不能保证,观察者对象的注册肯定是在被观察者注册之后,这样一个先后顺序。
所以需要添加一个,假如观察者,先进行注册,然后被观察注册,我需要把提前注册的观察者,放在后来注册被观察者的观察列表中。
六,观察者的提前注册
之前的代码并没有进行改动,主要新增了几句代码
在TheobservedPool脚本中,新增一个提前注册的观察者对象集合,然后两个方法中,增加了几句代码
/// <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 });
}
}
}
在Theobserved中新增了一个合并观察者列表的方法,引用了Systeam.Linq
/// <summary>
/// 合并观察者列表
/// </summary>
/// <param name="FrontObservers"></param>
public void UnionObservers(List<IObserver> FrontObservers)
{
Observers = Observers.Union(FrontObservers).ToList();
}
然后我把逻辑代码A中的wake改为Start,Start改成Awake,观察者先进行注册,被观察者再进行注册,效果还是一样。
但是需要注意的是,初始化属性的, TheobservedNames.InitializationAttributes();这一个方法必须最先执行
商品如果连名字都没有是没法上架的。
如果应用到项目里,要新增一些带参数的方法,不可能所有方法都是没参数的,观察者接口新增一个重载泛型方法,被观察者池里,添加一个泛型方法调用就行了。具体代码,就不上了。
完。