設計模式系列-外觀模式

 

一、上篇回顧

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

適合在哪些場景下使用呢?我們先來回顧一下我們上篇講述的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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章