接上文,既然一个服务有多个消费者,在具体的功能实现上,会遇到许多有细微差别的地方,比如:
对商品的修改有些服务需要短信通知后台管理员,有些服务需要通过邮件+站内消息的形式通知相关用户,有些服务要求无需任何通知操作。
为了能兼容这些矛盾,我们尝试去添加相应的接口如下:
public interface IOrder
{
//编辑
bool OrderEdit(Order entity);
//编辑附带短信
bool OrderEditWithSMS(Order entity);
//编辑附带站内消息和邮件
bool OrderEditWithMESSAGE_MAIL(Order entity);
}
一旦需求有了新的变化,比如,有的服务要求编辑商品后发送站内消息+邮件,有的服务要求发送短信+站内消息+邮件等等,就需要添加新的接口。这就好像是求解一道功能之间排列组合的问题,n个功能的结果就是n的阶乘。
我们现在选择在接口设计时就把结果一一列出,声明n的阶乘个方法。一不小心就违反了单一职责原则、接口隔离原则和开闭原则。
造成这种尴尬局面的根本原因是:除了最基本的编辑之外,其他的诸如短信、邮件、站内消息等附加功能是动态的,只有在调用时,才确定具体需要哪些附加功能。
是的,这里并不需要保证数据强一致性,什么短信、邮件、站内消息都是事件驱动模型里的东西,是异步调用的,并不会阻塞订单编辑操作,正是这些并不需要事务包裹的附加功能降低了模块的内聚。
基本思路:
方案1:不改变接口,而是创建一个包装对象,也就是装饰来包裹真实的对象,动态的扩展一个对象的功能=》装饰器模式
方案2:业务逻辑层添加一个消息接口,直接把消息发送功能暴露给调用者,调用者自行决定何时何地调用。
zhe
下面来看方案2大体是如何实现的:
1)定义的消息接口
public interface IMessage
{
void send(Message entity);
}
public class SMSBLL:IMessage
{
...
}
public class EmailBLL:IMessage
{
...
}
2)服务项目订单编辑:
public bool Edit(string id,Message entity)
{
if(IOrder.Edit(id))
{
//发送短信
SMS.Send(entity);
//发送邮件
Email.Send(entity);
return true;
}
return false;
}
观察者模式
观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并执行。观察者模式属于行为型模式。
目标对象(ISubject):订单
观察者(IObserver):短信、站内消息、邮件
1)封装观察者:
//封装消息体
public class Message
{
public string id{get;set;}
public string message{get;set;}
}
//观察者
public interface IObserver {
void send(Message entity);
}
//短信观察者
public class SMSObserver:IObserver
{
public void send(Message entity)
{
//调用短信服务
}
}
//站内消息观察者
public class MessageObserver:IObserver
{
public void send(Message entity)
{
//调用站内消息服务
}
}
。。。
2)创建目标对象接口,包括一个观察者队列属性和注册观察者、删除观察者、通知所有观察者三个方法,并在订单中实现目标对象接口:
public interface ISubject
{
IList<IObser> Observers{get;set;}
//注册
void RegisterObserver(IObserver o);
//删除
void RemoveObserver(IObserver o);
//通知
void NotifyObserver();
}
public interface IOrder:ISubject
{
IList<IObser> Observers{get;set;}
//编辑
bool OrderEdit(Order entity);
}
public class OrderBLL:IOrder
{
public IList<IObserver> Observers{get;set;}
public void RegisterObserver(IObserver o)
{
if(!Observers.Contains(O))
{
Observers.Add(o);
}
}
//删除
public void RemoveObserver(IObserver o)
{
if(!Observers.Contains(O))
{
Observers.Remove(o);
}
}
//通知所有观察者
public void NotifyObserver(Message entity)
{
foreach(var item in Observers)
{
item.send(entity);
}
}
}
类中实现了注册、删除和通知方法,用以维护观察者列表,基本满足了我们的需求。
结构型设计模式是从程序的结构上解决模块之间的耦合问题,如装饰器模式,在不改变原接口的情况下,通过新创建的类对原来的对象的进行扩展。而行为型设计模式是在不同对象之间划分责任和算法的抽象化,如观察者模式,通过对象关联的方式来分配多个职责。
我们在IOberserve这个接口中声明了Observers这个属性,目的是保证主题实现类中的消息列表属性必须统一使用IList类型,防止在不同的实现类里出现诸如数组、链表、队列、二叉树等等五花八门的类型。
那就是说主题实现类中的观察者列表是一个属性,是public类型,直接将这个列表暴露给了调用者,调用者可以直接操作订单对象的Observers属性,例如可以通过操作list类型自带的Add方法来注册,而不必通过类内部的实现的注册方法,在订单编辑之前,就可以通过遍历该属性就可以发送消息 。这破坏了类的封装,减弱代码的安全性。
我们需要对这个属性字段的进一步封装,来约束调用者只能通过主题类内部实现的注册、删除和通知方法来操作Observers属性。
事件包含一个私有的委托对象,只能在类内部触发,封装性和易用性特别好,这里我们采用事件优化之前的代码。
//消息委托
public delegate void SendEventHandler(object sender, EventArgs e);
//订单
public interface IOrder:ISubject
{
//事件
event SendEventHandler SendEvent;
//编辑
bool OrderEdit(Order entity);
}
public class OrderBLL:IOrder
{
public event SendEventHandler SendEvent;
public bool OrderEdit(Order entity)
{
var val=new OrderDal().OrderEdit(entity);
//事件广播
_SendEvent(message,null);
return val;
}
}
事件限制了委托对象只能通过类内部的事件访问器Add和Remove方法注册和删除,事件的广播只能通过调用类内部的OrderEdit方法。
同一般的属性,事件是也是通过事件访问器来封装的,非简写情况,代码如下:
public class OrderBLL:IOrder
{
private SendEventHandler _SendEvent;
public event SendEventHandler SendEvent
{
//注册
add
{
if(value!=null)
{
_SendEvent += value;
}
}
//删除
remove
{
if (value==_SendEvent)
{
_SendEvent -= value;
}
}
}
public bool OrderEdit(Order entity)
{
var val=new OrderDal().OrderEdit(entity);
//事件广播
_SendEvent(message,null);
return val;
}
}
可以发现,事件访问器的add和remove方法都是void类型的。而属性访问器里只有set方法是void类型,get方法有返回值,类型同字段类型,这才是事件封装性好的关键。