Unity中 简单实现观察者模式。

这里就不介绍观察者模式了,作为最基础的设计模式,概念是很简单的。

我自己看过好多博客,但是具体实现起来总是有些不知道怎么下手,我也是初学者,所以自己实现后,晒出来给大家参考。

一,思路

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();这一个方法必须最先执行

商品如果连名字都没有是没法上架的。

如果应用到项目里,要新增一些带参数的方法,不可能所有方法都是没参数的,观察者接口新增一个重载泛型方法,被观察者池里,添加一个泛型方法调用就行了。具体代码,就不上了。

完。

猜你喜欢

转载自blog.csdn.net/qq_41886896/article/details/99291404