1. 程式人生 > >跟著專案學設計模式(九) 裝飾器模式

跟著專案學設計模式(九) 裝飾器模式

接上文,既然一個服務有多個消費者,在具體的功能實現上,會遇到許多有細微差別的地方,比如:

對商品的修改有些服務需要簡訊通知後臺管理員,有些服務需要通過郵件+站內訊息的形式通知相關使用者,有些服務要求無需任何通知操作。

為了能相容這些矛盾,我們嘗試去新增相應的介面如下:

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();
    ......
    ......
}