跟著專案學設計模式(九) 裝飾器模式
阿新 • • 發佈:2018-11-06
接上文,既然一個服務有多個消費者,在具體的功能實現上,會遇到許多有細微差別的地方,比如:
對商品的修改有些服務需要簡訊通知後臺管理員,有些服務需要通過郵件+站內訊息的形式通知相關使用者,有些服務要求無需任何通知操作。
為了能相容這些矛盾,我們嘗試去新增相應的介面如下:
public interface IOrder { //編輯 bool OrderEdit(Order entity); //編輯附帶簡訊 bool OrderEditWithSMS(Order entity); //編輯附帶站內訊息和郵件 bool OrderEditWithMESSAGE_MAIL(Order entity); }
一旦需求有了新的變化,比如,有的服務要求編輯商品後傳送站內訊息+郵件,有的服務要求傳送簡訊+站內訊息+郵件等等,就需要新增新的介面。這就好像是求解一道功能之間排列組合的問題,n個功能的結果就是n的階乘。
我們現在選擇在介面設計時就把結果一一列出,宣告n的階乘個方法。一不小心就違反了單一職責原則、介面隔離原則和開閉原則。
造成這種尷尬局面的根本原因是:除了最基本的編輯之外,其他的諸如簡訊、郵件、站內訊息等附加功能是動態的,只有在呼叫時,才確定具體需要哪些附加功能。
是的,這裡並不需要保證資料強一致性,什麼簡訊、郵件、站內訊息都是事件驅動模型裡的東西,是非同步呼叫的,並不會阻塞訂單編輯操作,正是這些並不需要事務包裹的附加功能 降低了模組的內聚。
基本思路:
方案1:不改變介面,而是建立一個包裝物件,也就是裝飾來包裹真實的物件,動態的擴充套件一個物件的功能=》裝飾器模式
public interface IOrder
{
bool OrderEdit(Order entity);
}
//基本編輯功能
public class OrderBLL : IOrder
{}
//簡訊裝飾器
public class Order_SMSDecorator : IOrder
{}
方案2:業務邏輯層新增一個訊息介面,將這些附加功能封裝起來,暴露給服務端,將訊息傳送放到服務端去解決。
裝飾器模式
裝飾者模式(Decorator Pattern),是在不必改變原類檔案和使用繼承的情況下,動態的擴充套件一個物件的功能。它是通過建立一個包裝物件,也就是裝飾來包裹真實的物件。
public interface IOrder
{
bool OrderEdit(Order entity);
}
//基本編輯功能
public class OrderBLL : IOrder
{
bool IOrderBLL.OrderEdit(Order entity)
{
var val = new OrderDal().OrderEdit(entity);
return val;
}
}
//簡訊裝飾器
public class Order_SMSDecorator : IOrder
{
private IOrderBLL Order;
public Order_SMSDecorator(IOrderBLL order)
{
this.Order = order;
}
bool IOrderBLL.OrderEdit(Order entity)
{
var val = Order.OrderEdit(entity);
///////////////////
do 傳送簡訊
///////////////////
return val;
}
}
//站內訊息裝飾器
public class Order_MESSAGEDecorator : IOrder
{
private IOrder Order;
public Order_MESSAGEDecorator(IOrderBLL order)
{
this.Order = order;
}
bool IOrderBLL.OrderEdit(Order entity)
{
var val=Order.OrderEdit(entity);
///////////////////
do 傳送站內訊息
///////////////////
return val;
}
}
//郵件裝飾器
public class Order_MAILDecorator : IOrder
{
private IOrder Order;
public Order_MAILDecorator(IOrder order)
{
this.Order = order;
}
bool IOrderBLL.OrderEdit(Order entity)
{
var val = Order.OrderEdit(entity);
///////////////////
do 傳送郵件
///////////////////
return val;
}
}
我們來組合出不同的功能
var order=new OrderBLL();
//簡訊
var order_SMS=new Order_SMSDecorator(orderBLL);
//郵件
var order_MAIL=new Order_MAILDecorator(orderBLL);
//郵件+站內訊息
var order_MAIL_MESSAGE=new Order_MESSAGEDecorator(order_MAIL)
好了,在最終的服務專案中呼叫的時候,直接在IOC裡組裝成不同功能的物件就可以了,這裡用的是輕量級的AutoFac,可以根據name的不同獲取相應的物件:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
......
......
var order=new OrderBLL();
//簡訊
var order_SMS=new Order_SMSDecorator(orderBLL);
//郵件
var order_MAIL=new Order_MAILDecorator(orderBLL);
//郵件+站內訊息
var order_MAIL_MESSAGE=new Order_MESSAGEDecorator(order_MAIL)
builder.Register(o=>order).As<IOrder>()
.Named<IOrder>("訂單").SingleInstance();
builder.Register(o=>order_SMS).As<IOrder>()
.Named<IOrder>("訂單+簡訊").SingleInstance();
builder.Register(o=>order_MAIL_MESSAGE).As<IOrder>()
.Named<IOrder>("訂單+站內訊息+郵件").SingleInstance();
......
......
}