您的位置:首页 > 其它

设计模式学习:观察者模式

2017-06-02 14:20 507 查看
观察者模式在开发中是一个普遍应用的设计模式。在我看来,观察者模式主要解决的问题就是解耦,在团队开发中,一个模块可能会有很多不同的功能,而这些功能又有可能被分配到了不同的人来开发。利用观察者模式,可以实现多人的协同开发。每个人只需要按照规则写好自己的功能,并在主题中加入自己的观察者,就可以实现对方法的调用。

传统的观察者模式代码(在开发中没有这么写过):

/// <summary>
/// 主题接口
/// </summary>
public interface ISubject
{
void AddListener(IObserver observer);
void RemoveListener(IObserver observer);
void Update();
}
/// <summary>
/// 观察者接口
/// </summary>
public interface IObserver
{
void Excute();
}

/// <summary>
/// 主题的基类
/// </summary>
public class MySubject: ISubject
{
List<IObserver> list;

public MySubject()
{
list = new System.Collections.Generic.List<IObserver>();
}
//添加观察者
public void AddListener(IObserver observer)
{
list.Add(observer);
}
//删除观察者
public void RemoveListener(IObserver observer)
{
list.Remove(observer);
}
//实现调用所有的观察者中的方法
public void Update()
{
for (int i = 0; i < list.Count; i++)
{
list[i].Excute();
}
}

}

/// <summary>
/// 具体的主题
/// </summary>
public class ConcreteSubject : MySubject
{
public  string food = "苹果";
public  string milk = "奶";
}

/// <summary>
/// 具体的观察者
/// </summary>
public class EatObserver : IObserver
{
ConcreteSubject subject;

//实现这个构造器应该是为了获取主题里的数据,并加以利用。其实在我看来并没什么用
public EatObserver(ConcreteSubject subject)
{
this.subject = subject;
}
public void Excute()
{
Debug.Log("Eat:"+subject.food);
}
}
/// <summary>
/// 具体的观察者
/// </summary>
public class DrinkObserver : IObserver
{
ConcreteSubject subject;
public DrinkObserver(ConcreteSubject subject)
{
this.subject = subject;
}
public void Excute()
{
Debug.Log("Drink:" + subject.milk);
}
}


测试:

ConcreteSubject subject = new ConcreteSubject();
IObserver eatobserver = new EatObserver(subject);
IObserver drinkobserver = new DrinkObserver(subject);

subject.AddListener(eatobserver);
subject.AddListener(drinkobserver);

subject.Update();




而在实际的开发中,都是实现了消息的注册和广播。

下面是一个简单的消息广播,是我写的测试类。正常开发的过程中也是类似于这种

/// <summary>
/// 消息类型
/// </summary>
public enum Msg
{
msg_Exit,
msg_login,
}

/// <summary>
/// 广播
/// </summary>
public class EventHandle
{
private static EventHandle instance;

public static EventHandle Instance
{
get
{
if (instance == null)
instance = new EventHandle();
return instance;
}

}

//这里只提供了2个委托,正常情况是4个参数就够用了
public delegate void CallBack();
public delegate void CallBack<T>(T t1);

//观察者模式中储存观察者的列表
public Dictionary<Msg, Delegate> dic = new Dictionary<Msg, System.Delegate>();

/// <summary>
/// 添加消息类型和委托
/// </summary>
/// <param name="m"></param>
/// <param name="add"></param>
public void AddListener(Msg m, CallBack add)
{
AddingListener(m, add);
dic[m] = (CallBack)dic[m] + add;
}
void AddingListener(Msg m, Delegate add)
{
if (dic.ContainsKey(m))
{
Delegate d = dic[m];
if (dic[m] != null && dic[m].GetType() != add.GetType())
{
Debug.Log("Delegate'Type is not Same");
}

}
else
{
dic.Add(m, null);
}
}
public class WarnningException : Exception
{
public WarnningException(string msg)
: base(msg)
{
}
}
/// <summary>
/// 带一个参数的,其他带参数的可以自己补充
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="m"></param>
/// <param name="add"></param>
public void AddListener<T>(Msg m, CallBack<T> add)
{
AddingListener(m, add);
dic[m] = (CallBack<T>)dic[m] + add;
}

/// <summary>
/// 实现对应消息的委托方法
/// </summary>
/// <param name="m"></param>
public  void Broadcast(Msg m)
{
if (!dic.ContainsKey(m))
{
throw new WarnningException("Dont Have this MsgType!");
}
Delegate d;
if (dic.TryGetValue(m, out d))
{
CallBack callback = d as CallBack;

if (callback != null)
{
callback();
}
else
{
throw new WarnningException("CallBack is Null!");
}
}
}
public  void Broadcast<T>(Msg m,T arg1)
{
if (!dic.ContainsKey(m))
{
throw new WarnningException("Dont Have this MsgType!");
}
Delegate d;
if (dic.TryGetValue(m, out d))
{
CallBack<T> callback = d as CallBack<T>;

if (callback != null)
{
callback(arg1);
}
else
{
throw new WarnningException("CallBack is Null!");
}
}
}
}


测试:

public class test : MonoBehaviour
{

public void Start()
{
//注册消息
EventHandle.Instance.AddListener<string>(Msg.msg_login, Login);
EventHandle.Instance.AddListener(Msg.msg_Exit, Exit);

//广播消息
EventHandle.Instance.Broadcast<string>(Msg.msg_login,"登录");
EventHandle.Instance.Broadcast(Msg.msg_Exit);
}

public void Login(string name)
{
//登录相关的操作
Debug.Log(name);
}
public void Exit()
{
Debug.Log("退出");
}
}




这里没有实现RemoveListener方法,实现方式和 AddListener相似。这样写方便阅读和使用。

还有一种经常使用的,就是事件访问器,event里提供了add和remove的方法,方便了主体和相关逻辑的分离。

下面上代码:

public class test : MonoBehaviour
{

public static test instance;

void Awake()
{
instance = this;
}

public delegate void TouchDelegate();

event TouchDelegate handle=null;

public event TouchDelegate TouchEvent
{

add
{
handle += value;
}
remove
{
handle -= value;
}

}
void Update()
{
if (handle != null)
handle();

}

}


这个类就相当于观察者模式中的主题类。里面的handle相当于链表,观察者将方法注入进去,在需要的时候,就可以调用这个handle里所有观察者的方法。

下面这个类相当于观察者,向主题注入方法。

public class ShowTest : MonoBehaviour {

void Start()
{

test.instance.TouchEvent += show;
Debug.Log("Awake");
}

void show()
{
Debug.Log("SHOW");
}
}


测试结果:



这个方法实现了逻辑与主题的分离。

unity里MonoBehaviour不能new(),所以单利要写在Awake或者Start里,否则new不出来。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: