設計模式系列-外觀模式

weixin_34026276發表於2017-11-12

一、上篇回顧

上篇我們主要講述了建立型模式中的最後一個模式-原型模式,我們主要講述了原型模式的幾類實現方案,和原型模式的應用的場景和特點,原型模式

適合在哪些場景下使用呢?我們先來回顧一下我們上篇講述的3個常用的場景。

      1、我們在執行態的時候,動態的建立一個動態型別的物件的時候,可能我們使用原型模式,可以動態的建立指定型別的副本,這無疑是好的選擇,否

則如果通過我們前面講述的幾個建立型模式來實現的話,效率和代價上是非常大的。

      2、有的時候我們需要對比一個物件在處理前和處理後進行物件狀態的對比,對比是否處理後物件的狀態是否發生變化,或者是其他的要求。這個時候

通過原型模式來克隆物件的副本,遠比通過引入其他的Factory或者abstract Factory 來的有效和更容易實現。

      3、如果我們發現有一類這樣的物件,這類物件通常來說比較簡單,並且這類物件之間的差別很有規律,並且這類物件數量一般有限,那麼這個時候,

我們通過原型模式來做的話,通過一個物件來複制建立其他型別的物件可能比通過引入其他的Factory或者abstract Factory 更容易實現,而且只需要對

象本身提供一個Clone()方法即可。

      4、有的時候我們的專案中有這樣的情況,我們是在別人的功能的基礎上進行擴充套件,我們有不能修改現有的程式,如果這個應用程式是基於其他型別的

建立型模式,那麼如果我們在系統中新增一個型別的時候,我們需要修改統一的建立型模式中的程式碼,不管是修改配置檔案還是具體的功能程式碼,無疑都是

要修改的,那麼如果我們通過原型模式的話,只需要在新增型別的物件內部,提供一個克隆方法即可,完成新物件的建立。

通過上面的情況,那麼我們也能大概看出來原型模式的有一個前提,就是必須是基於物件之上呼叫Clone()方法完成物件的複製,如果沒有建立這個對

象的話,可能不能直接使用該方法。

我們也講述了,對於Clone()物件的時候,深複製和淺複製的情況,還包括通過序列化物件的形式來完成物件的深複製。

二、摘要

本文主要是講述結構型模式中一個比較常用的模式-外觀模式,這個模式呢,有個最大的特點將細粒度的物件包裝成粗粒度的物件,應用程式通過

訪問這個外觀物件,來完成細粒度物件的呼叫,外觀模式一般是分散式應用和系統架構中的應用服務層的設計中常用的方式,並且一般結合外觀模式+DTO

來完成服務層的設計,提供分散式應用服務的高效服務,外觀模式我們可以這樣理解,我們通過外觀的包裝,使應用程式只能看到外觀物件,而不會看到具

體的細節物件,這樣無疑會降低應用程式的複雜度,並且提高了程式的可維護性。本文將會從以下幾個方面進行講述:

       1、外觀模式的使用場景和特點

       2、外觀模式的實現方案。

       3、總結外觀模式。

我們這裡先給出一個外觀模式的原理圖:

clip_image001這是未使用外觀模式之前的情況,下面給出使用外觀模式後的情形:

clip_image002通過外觀物件來組織細粒度的服務的呼叫,外觀物件提供給外部應用程式可

以使用的服務,而具體的呼叫細粒度的過程則被外觀物件給封裝起來,當然這個過程就是封裝變化的部分,而將變化的部分與應用程式進行隔離,無疑對程

序的易用性和可維護性都是很大的提高。

三、本文大綱

       a、上篇回顧。

       b、摘要。

       c、本文大綱。

       d、外觀模式的特點及使用場景。

       e、外觀模式的實現方案。

       f、外觀模式使用總結。

       g、系列進度。

       h、下篇預告。

四、外觀模式的特點及使用場景

外觀模式的主要思想是將複雜的細粒度的物件服務包裝成簡單的易使用的粗粒度的功能服務,我們大家最容易理解和知道的外觀模式就是,使用的API

介面的封裝,我們將第三方的API介面引入到我們的專案中的時候,我們需要對這些介面進行包裝,將細粒度的具體呼叫過程進行包裝成外觀類的形式,通

過外觀類來進行統一的呼叫。我們平時把一些常用的公共方法也可以簡易的稱之為外觀模式,我們將複雜的細粒度的功能,包裝成一個比較通用的簡易的的

粗粒度的功能。我們來看看哪些場景下,我們使用外觀模式很適合呢?

      1、我們在使用第三方類庫或者API的時候,我們通過本地的API介面的封裝,來完成對第三方API介面的粗粒度外觀物件,通過這個外觀物件可以很容

易的完成服務的呼叫。我們這裡舉例說明吧,例如現在我有一個傳送手機簡訊的API介面,是第三方提供給我的API介面,那麼我如何包裝呢?

下面給出對API封裝的相關程式碼和說明

    public class MessageHelper 
    { 
        private static readonly MessageHelper instance = new MessageHelper();

        #region API介面

        [DllImport("EUCPComm.dll", EntryPoint = "SendSMS")]  //即時傳送 
        private static extern int SendSMS(string sn, string mn, string ct, string priority);

        [DllImport("EUCPComm.dll", EntryPoint = "SendSMSEx")]  //即時傳送(擴充套件) 
        private static extern int SendSMSEx(string sn, string mn, string ct, string addi, string priority);

        [DllImport("EUCPComm.dll", EntryPoint = "SendScheSMS")]  // 定時傳送 
        private static extern int SendScheSMS(string sn, string mn, string ct, string ti, string priority);

        #endregion

        #region 對上面的API包裝後的方法

        public int SendSMSEx1(string sn, string mn, string ct, string addi, string priority) 
        { 
            return SendSMSEx(sn, mn, ct, addi, priority); 
        }

        public int SendSMS1(string sn, string mn, string ct, string priority) 
        { 
            return SendSMS(sn, mn, ct, priority); 
        }

        public int SendScheSMS1(string sn, string mn, string ct, string ti, string priority) 
        { 
            return SendScheSMS(sn, mn, ct, ti, priority); 
        }

        #endregion 
    }

相關的測試程式碼:

      static void Main(string[] args) 
       { 
           //相關的測試程式碼 
           //呼叫外觀物件中的服務 
           MessageHelper.instance.SendSMS1("", "", "", ""); 
       }

      2、我們在架構設計的過程中,一次的功能訪問可能需要同時的呼叫很多個物件,那麼如果我們在服務呼叫的時候,能夠在應用程式呼叫中一次就能完

成所有要同時呼叫的物件那該多好啊,外觀模式無疑是最好的原則,特別是在分散式應用中,通過遠端呼叫服務,通過外觀模式降低應用程式與服務的互動

次數,同時可以降低應用程式的複雜性。外觀模式+DTO,提供遠端服務呼叫的效能,這些都是好的設計方式。我們來看看簡單的示例吧,我想我們更

能瞭解其中的奧妙。看圖說話吧,我們這裡以一次遠端同步服務為例。

未使用外觀模式前:

clip_image003一個簡單的同步服務,由於應用程式在這次服務中為了完成同步操作,必須進行3次遠端連線來進

行把3個物件進行同步,那麼如果我們使用外觀物件之後呢,我們只需要訪問一次即可完成3個物件的同步。這無疑提高了系統的效能和效率。

clip_image004並且通過DTO來進行傳輸的話,可以提供遠端服務呼叫的訪問此時和效率。

五、外觀模式的實現方案

      5.1 外觀模式的經典實現

我們先來看看外觀模式的經典實現,我們這裡已二進位制流序列化服務外觀為例來說明下經典實現吧

定義一個二進位制序列化外觀類:

    public class SerializationFacede 
    { 
        public string BinarySerializationObjToString(object target) 
        { 
            using (MemoryStream stream = new MemoryStream()) 
            { 
                new BinaryFormatter().Serialize(stream, target);

                byte[] targetArray = stream.ToArray();

                return Convert.ToBase64String(targetArray); 
            } 
        }

        public object DerializationStringToObj(string target) 
        { 
            byte[] targetArray = Convert.FromBase64String(target); 
            using (MemoryStream stream = new MemoryStream(targetArray)) 
            { 
                return new BinaryFormatter().Deserialize(stream); 
            } 
        }

         public T Derialization<T>(string target) 
        { 
            return (T)DerializationStringToObj(target); 
        } 
    }

我們這裡給出相關的測試程式碼:

       static void Main(string[] args) 
        { 
            //外觀類 
            SerializationFacede facede = new SerializationFacede(); 
            //測試物件類 
            Product product = new Product();

            //序列化物件 
            string productString=  facede.BinarySerializationObjToString(product); 
            //反序列化物件 
            product = facede.Derialization<Product>(productString); 
        }

通過上面的程式碼我們可以看出,其實外觀類也可以以靜態方法的形式來提供,提供一個統一的訪問入口,這裡可以使用我們前面講述的單例模式來解決這個

問題。或者提供一個外觀物件的建立型工廠來完成建立,而不是通過new()的方式來使用。

我們可以改進一下上面的外觀模式,通過定義介面,來解耦客戶端程式的呼叫,通過提供一個介面,客戶呼叫通過介面的形式來呼叫,無疑就可以解

決這樣依賴關係:

我們先來看看介面的定義形式:

    /// <summary> 
    /// 序列化服務的外觀介面 
    /// </summary> 
    public interface ISerializationFace 
    { 
        string SerializableToString(object target);

        object DerializableToObject(string target);

        T Derializable<T>(string target); 
    }

我們分別實現SOAP與二進位制的形式來實現我們定義的服務外觀介面:

二進位制序列化服務:

    public class BinarySerializationFace : ISerializationFace 
    { 
        #region ISerializationFace 成員

        public string SerializableToString(object target) 
        { 
            throw new NotImplementedException(); 
        }

        public object DerializableToObject(string target) 
        { 
            throw new NotImplementedException(); 
        }

        public T Derializable<T>(string target) 
        { 
            throw new NotImplementedException(); 
        }

        #endregion 
    }

SOAP序列化服務:

    public class SoapSerializationFace : ISerializationFace 
    { 
        #region ISerializationFace 成員

        public string SerializableToString(object target) 
        { 
            throw new NotImplementedException(); 
        }

        public object DerializableToObject(string target) 
        { 
            throw new NotImplementedException(); 
        }

        public T Derializable<T>(string target) 
        { 
            throw new NotImplementedException(); 
        }

        #endregion 
    }

測試程式碼如下:

        static void Main(string[] args) 
        { 
            //外觀類 
            ISerializationFace facede = new SoapSerializationFace(); 
            //測試物件類 
            Product product = new Product();

            //序列化物件 
            string productString = facede.SerializableToString(product); 
            //反序列化物件 
            product = facede.Derializable<Product>(productString); 
        }

這樣我們就提高了外觀模式的靈活性。

       5.2、外觀模式的其他考慮

             5.2.1-傳統外觀模式的擴充套件

上面給出的外觀模式,我們在具體的測試程式碼中還是直接通過new()具體的序列化物件的形式,我們這裡可以進行改進,通過工廠模式,或者是通過配

置檔案的形式來解耦,一般可能外觀類不多的情況下,通過配置檔案的方式來進行解耦是不錯的選擇,而不用提供單獨的工廠去建立。

我們可以通過如下的形式來改進上面的方案,例如我們的配置檔案的格式定義如下:

<?xml version="1.0" encoding="utf-8" ?> 
<Serialization> 
  <SerializationSection name="serialization" type="SerializationType"/> 
  <SerializationSection /> 
</Serialization>

那麼我們看看序列化工廠帶示例程式碼

    public class SerializationFactory 
    { 
        public static ISerializationFace Create() 
        { 
            //配置檔案中讀取的Type型別 
            string type = XMLHelper.GetSectionValue(""); 
            return (ISerializationFace)Activator.CreateInstance(Type.GetType(type)); 
        } 
    }

我們來看看具體的測試程式碼:

        static void Main(string[] args) 
        { 
            //外觀類 
            ISerializationFace facede = SerializationFactory.Create(); 
            //測試物件類 
            Product product = new Product();

            //序列化物件 
            string productString = facede.SerializableToString(product); 
            //反序列化物件 
            product = facede.Derializable<Product>(productString); 
        }

              5.2.2-傳統外觀模式的擴充套件

下面給我們來看看另外一種形式的外觀模式中的DTO+外觀模式的例項實現程式碼

我們看看DTO的形式:

    /// <summary> 
    /// DTO(資料傳輸物件),也可以稱之為資料載體 
    /// </summary> 
    public class DTO 
    {

        private Product _product; 
        private List<Order> _orderList; 
        //全部都是資料資訊或者是其他的引用物件資訊 
        public Product Product 
        { 
            get 
            { 
                return this._product; 
            } 
            set 
            { 
                this._product = value; 
            } 
        } 
        public List<Order> Product 
        { 
            get 
            { 
                return this._orderList; 
            } 
            set 
            { 
                this._orderList = value; 
            } 
        }

    }

具體使用DTO物件的外觀類程式碼如下:

   /// <summary> 
   /// 遠端訪問服務 
   /// </summary> 
   public class AccessService 
   { 
       bool GetService(DTO dto) 
       { 
           return true; 
       }

       DTO GetData() 
       { 
           return new DTO(); 
       } 
   }

通過這樣的簡易的方式即可完成服務的訪問。

程式的測試程式碼如下:

        static void Main(string[] args) 
        { 
            //外觀類 
            AccessService service = new AccessService();

            DTO dto= service.GetData(); 
            //TODO... 
        }

通過上面的程式碼,我們完成了最簡單的遠端外觀的訪問服務,遠端的外觀服務還可以通過WCF的形式來完成,由於WCF內建整合了Remoting和

WebService的形式呼叫,或者是socket的形式。我這裡就不詳細的介紹了

六、外觀模式使用總結

外觀模式作為結構型模式中的一個簡單又實用的模式,外觀模式通過封裝細節來提供大粒度的呼叫,直接的好處就是,封裝細節,提供了應用寫程式

的可維護性和易用性。外觀模式一般應用在系統架構的服務層中,當我們是多個不同型別的客戶端應用程式時,比如一個系統既可以在通過Web的形式訪

問,也可以通過客戶端應用程式的形式時,可能通過外觀模式來提供遠端服務,讓應用程式進行遠端呼叫,這樣通過外觀形式提供服務,那麼不管是什麼樣

的客戶端都訪問一致的外觀服務,那麼以後就算是我們的應用服務發生變化,那麼我們不需要修改沒一個客戶端應用的呼叫,只需要修改相應的外觀應用即

可。

七、系列進度

八、下篇預告

下篇將會針對外觀模式進行講述,該模式也是結構型模式中很有特點設計模式之一,該 模式是將現有系統中的一些細粒度的東西通過外觀物件包裝起來,

在應用程式中訪問這些方法的時候,通過外觀類的形式,提供統一的訪問入口,並且具體的細節,應用程式並不需要知道,這樣就會降低程式呼叫的複雜

性,由於本人水平有限,不足或者有錯誤的地方,請大家批評指正,請大家繼續支援我,謝謝。

九、Demo下載

       本文Demo



本文轉自 hot的fans  51CTO部落格,原文連結:http://blog.51cto.com/2435232/603392

相關文章