小话设计模式(番外二)委托模式

来源:互联网 发布:fd软件下载 编辑:程序博客网 时间:2024/06/05 21:51

委托(Delegate)模式定义了对象之间的一对一的关系,被委托方可以作为委托方的事件接收者或者数据源(Data Source),当它作为事件接受者的时候,可以认为它是一种特殊的观察者(参考小话设计模式(十七)观察者模式)。

有人认为委托模式和代理模式是一个东西。(⊙o⊙)…好吧,可能是因为觉得Delegate的部分发音像代理而且可以将就着翻译成代理,但是实际上这是不同的两种模式(参考小话设计模式(十二)代理模式)。

委托模式在IOS和Cocos2d-x(实际上就是借鉴了IOS的做法)得到了广泛的应用,二者分别用protocol(协议)和虚函数/纯虚函数实现被委托方的接口。而在C#里面可以用interface或者delegate(虽然是同名,但实际上是观察者模式的应用,参考C#语法小知识(四)委托delegate)。

考虑这种情况,游戏中有一个商店,可以出售货物,而我们希望货物的数据源可以更换,那么就可以考虑使用委托模式。

数据源接口:

public interface IShopDataSource{string GetItem (int idx);int ItemCount();}

如果我们希望道具卖出时可以处理一些事件,那么可以定义委托接口:

public interface IShopDelegate{void ItemSold (Shop shop, int idx);}
商店定义:

public class Shop{public Shop(IShopDataSource dataSource_, IShopDelegate shopDelegate_){dataSource = dataSource_;shopDelegate = shopDelegate_;}public IShopDataSource dataSource { get; set; }public IShopDelegate shopDelegate{ get; set;}public void ShowItemList(){if (dataSource == null) {return;}int count = dataSource.ItemCount ();for (int i = 0; i < count; i++) {Console.WriteLine (dataSource.GetItem(i));}}public void SellItem(int idx){if (shopDelegate == null) {return;}shopDelegate.ItemSold (this, idx);}}
商店数据源定义:

public class ShopDataSource : IShopDataSource, IShopDelegate{List<string> _items = new List<string>();public ShopDataSource(params string[] args){_items.AddRange (args);}public string GetItem (int idx){if (idx >= _items.Count) {return null;}return _items[idx];}public int ItemCount(){return _items.Count;}public void ItemSold(Shop shop, int idx){if (idx >= _items.Count) {return;}_items.RemoveAt (idx);shop.ShowItemList ();}}
使用:

ShopDataSource sds = new ShopDataSource("Apple", "Pen", "Pineapple");Shop shop = new Shop (sds, sds);shop.ShowItemList ();shop.SellItem (1);

可能会有人问,既然ShopDataSource继承了两个接口,那么为什么不把这两个接口合并呢?
当我们希望商店可以无限卖出道具的时候,那么ShopDataSource再继承自IShopDelegate就没有什么意义了。
当然,我们也可以声明一个ShopDelegate,它就负责显示第几个货物被卖出了:

public class ShopDelegate : IShopDelegate{public void ItemSold(Shop shop, int idx){Console.WriteLine (idx);}}
而且,我们可以设置shop的dataSource或者shopDelegate来修改数据源或事件接收者。


委托模式的好处,可以实现对象具体逻辑和被委托方(数据源和事件接收者)之间的松耦合,这一点在IOS的UITableView和cocos2d-x的TableView里得到了很好的体现。

坏处在于,如果接口方法太多,那么被委托方的实际类可能会需要实现很多无关的方法,尤其对于c#来讲。

不过使用c#的delegate可以很好的解决这个问题。

新的商店定义:


public class NewShop{public delegate int DelegateItemNum();public delegate string DelegateGetItem(int idx);public delegate void DelegateItemSold(NewShop shop, int idx);public DelegateItemNum itemNumDelegate { protected get; set;}public DelegateGetItem getItemDelegate { protected get; set;}public DelegateItemSold itemSoldDelegate { protected get; set;}public void ShowItemList(){if (itemNumDelegate == null || getItemDelegate == null) {return;}int count = itemNumDelegate ();for (int i = 0; i < count; i++) {Console.WriteLine (getItemDelegate(i));}}public void SellItem(int idx){if (itemSoldDelegate == null) {return;}itemSoldDelegate (this, idx);}}

为了保证一对一的关系,我们将三个delegate的属性设置为可写不可读(屏蔽了+=和-=操作),因为对于一个商店来讲添加多个数据源在大多数情况下并不合适,但实际上itemSoldDelegate这个方法并不需要这样做。

新的被委托方定义:

public class NewShopDelegate{List<string> _items = new List<string>();public NewShopDelegate(NewShop newShop, params string[] args){newShop.itemNumDelegate = ItemCount;newShop.getItemDelegate = GetItem;newShop.itemSoldDelegate = (shop, idx) => {if (idx >= _items.Count) {return;}_items.RemoveAt (idx);shop.ShowItemList ();};_items.AddRange (args);}public string GetItem (int idx){if (idx >= _items.Count) {return null;}return _items[idx];}public int ItemCount(){return _items.Count;}}
使用:

NewShop newShop = new NewShop ();NewShopDelegate nsd = new NewShopDelegate(newShop, "Apple", "Pen", "Pineapple");newShop.itemSoldDelegate = (newShop_, idx) => {Console.WriteLine(idx);};newShop.ShowItemList ();newShop.SellItem (1);



1 0