观察者模式和委托事件

观察者模式

定义

观察者模式(Observer Pattern)——在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

具体的说目的是 “类A想在类B变化的时候得到消息,从而跟着变化”

最简单的方式,双向关联:

​ 类A知道类B,并在一定时期将自身告诉类B

​ 类B持有类A的对象,并在自身变化的时候调用类A的方法

​ 这种方式不好的地方在于造成了双向关联,但类A想知道类B,类B其实并不想知道类A

观察者模式是这样的

​ 类B不再关心是类A还是类C、类D等等需要监听,类B只知道有类需要被通知消息,他把这些类叫做观察者,于是类B持有一个“观察者队列”,并提供一个注册的方法

​ 类A继承观察者接口,在需要的时候注册自己,并实现接口中被通知的方法

​ 类B在自身变化时依次通知队列中的所有人

UML

在这里插入图片描述

代码

首先定义一个观察者接口,有一个接收通知的方法

namespace DesignModel.ObserverPattern
{
	public interface IObserver  
	{
		void Notify();
	}
}

然后是我们被观察的类

namespace DesignModel.ObserverPattern
{
	public class Subject
	{
        //观察者队列
		private List<IObserver> container = new List<IObserver>();
		
        //观察者注册方法
		public void RegisterObserver(IObserver observer)
		{
			if (observer != null && !container.Contains(observer))
			{
				container.Add(observer);
			}
		}
		
        //观察者移除方法
		public void RemoveObserver(IObserver observer)
		{
			if (container.Contains(observer))
			{
				container.Remove(observer);
			}
		}

        //通知
		private void NotifyObserver()
		{
			foreach (var item in container)
			{
				item.Notify();
			}
		}
		
        //在某种情况下发出通知
		public void OnClick()
		{
			NotifyObserver();
		}
	}
}

以及观察者的实现

namespace DesignModel.ObserverPattern
{
	public class ConcreteObserver :IObserver
	{
		private Subject subject;
		public ConcreteObserver(Subject subject) 
		{
			this.subject = subject;
			//向subject注册自己
			subject.RegisterObserver(this);
		}
		public void Notify()
		{
			Debug.Log("收到通知!");
		}
	}
}

简单测试

using DesignModel.ObserverPattern;
public class Client_Observer_Example1 : MonoBehaviour
{
	void Start () 
	{
		Subject subject = new Subject();
		ConcreteObserver observer = new ConcreteObserver(subject);

		subject.OnClick();
	}
}
观察者模式的总结

优点:

1.将被观察者的依赖从具体的类变为依赖抽象,从而使得被观察者不认识任何一个观察者而仅知道观察者接口,降低了耦合

2.观察者模式可以支持广播,使得被观察者可以通知到每一个观察者

缺点:

1.需要注意观察者与被观察者之间不能有循环依赖,否则容易导致程序卡住

2.如果有太多的观察者,则通知每一个观察者需要耗费大量时间

委托事件

定义

C#为我们提供了更方便的通知模式,即委托和事件

当一个类作为被观察者,有通知观察者的需要的情况下,他只需要定义一个委托事件,并在需要时调用委托事件即可

作为观察者,需要知道被观察者,并将自己的处理通知的方法添加到事件中去即可

委托

delegate字段定义委托,相当于一个委托类型定义

事件

event字段定义事件,相当于一个委托类型的回调函数对象

不使用event字段,依然可以定义delegate委托类型的对象,但这是不安全的,被event字段标记的属性对象,只能通过±来添加或移除注册函数,而不能被赋值

代码

定义被观察者

public class NotifySender
{
	//委托
	public delegate void TestHandler();
    //事件
	public event TestHandler testEvent;

	public void TestEvent()
	{
		if (testEvent != null)
			testEvent();
	}
}

观察者

public class NotifyListener
{
	private NotifySender sender;
	public NotifyListener(NotifySender sender)
	{
		this.sender = sender;
		//注册事件
		this.sender.testEvent += Notify;
	}
	private void Notify()
	{
		Debug.Log("收到通知!");
	}	
}

客户端

public class Client_Observer_Delegate_Event:MonoBehaviour
{
	private void Start()
	{
		NotifySender sender = new NotifySender();
		NotifyListener listener = new NotifyListener(sender);

		sender.TestEvent();
	}
}

消息中心

利用委托事件实现一个消息中心,方便页面之间通信

数据模型和常量

namespace DesignModel.ObserverPattern
{
	public class NotificationModel 
	{
		public string name;
		public object[] param;
	}
	
	public class NotifyConst 
	{
		public const string TEST = "test";
		public const string TEST_PARAM = "test_param";
	}
}

消息中心

namespace DesignModel.ObserverPattern
{
	public class NotificationCenter  
	{
        //单例
		private static NotificationCenter instance;
		public static NotificationCenter Instance
		{
			get
			{
				if (instance == null)
					instance = new NotificationCenter();
				return instance;
			}
		}
		
        //委托
		public delegate void NotifyHandler(NotificationModel model);
        //消息字典
		private Dictionary<string, NotifyHandler> notifications;
        
		private NotificationCenter()
		{
			notifications = new Dictionary<string, NotifyHandler>();
		}
		
        /// <summary>
		/// 注册监听
		/// </summary>
		/// <param name="notifyName">要监听的消息名称</param>
		/// <param name="handler">监听函数</param>
		public void RegisterNotification(string notifyName, NotifyHandler handler)
		{
			if (!notifications.ContainsKey(notifyName))
			{
				NotifyHandler notification = null;
				notifications.Add(notifyName, notification);
			}
			notifications[notifyName] += handler;	
		}
		
        /// <summary>
		/// 移除监听
		/// </summary>
		/// <param name="notifyName">移除的消息名称</param>
		/// <param name="handler">要移除的监听函数</param>
		public void RemoveNotification(string notifyName,NotifyHandler handler)
		{
			if (notifications.ContainsKey(notifyName))
			{
				notifications[notifyName] -= handler;	

				if (notifications[notifyName] == null)
				{
					notifications.Remove(notifyName);
				}
			}
		}

        /// <summary>
		/// 发送消息
		/// </summary>
		/// <param name="notifyName">消息名称</param>
		public void SendNotification(string notifyName)
		{
			SendNotification(notifyName, null);
		}
		
        /// <summary>
		/// 发送消息
		/// </summary>
		/// <param name="notifyName">消息名称</param>
		/// <param name="objs">参数列表</param>
		public void SendNotification(string notifyName, params object[] objs)
		{
			if (notifications.ContainsKey(notifyName))
			{
				NotificationModel model = new NotificationModel();
				model.name = notifyName;
				model.param = objs;
				notifications[notifyName](model);
			}
		}
	}
}

使用测试

//监听类
public class Client_Observer_Listener : MonoBehaviour 
{
	private void Awake()
	{
		RegiserNotify();
	}
	
	//注册监听
	private void RegiserNotify()
	{
		NotificationCenter.Instance.RegisterNotification(NotifyConst.TEST, NotifyMethod);
		NotificationCenter.Instance.RegisterNotification(NotifyConst.TEST_PARAM, NotifyMethod);
	}
	
	//监听函数
	private void NotifyMethod(NotificationModel model)
	{
		switch(model.name)
		{
			case NotifyConst.TEST:
			{
				ShowMessage(model);
			}
			break;
			case NotifyConst.TEST_PARAM:
			{
				ShowMessage(model);
			}
			break;
		}
	}

	private void ShowMessage(NotificationModel model)
	{
		Debug.Log(model.name + ":");		
		if (model.param != null)
		{
			for (int i = 0; i < model.param.Length; i++)
			{
				Debug.Log(model.param[i]);
			}
		}
	}
}

//发送消息的类
public class Client_Observer_Sender : MonoBehaviour 
{
	void Start () 
	{
		//无参消息
		NotificationCenter.Instance.SendNotification(NotifyConst.TEST);
		//带参消息
		NotificationCenter.Instance.SendNotification(NotifyConst.TEST_PARAM, "param 1", "param 2");
	}
}

我们可以将注册过程抽象为一个接口,或者用模板方法模式在抽象中完成注册过程,从而简化使用

消息中心可以将监听对象和监听者完全解藕

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值