二十年,专注于中国最专业的模型驱动解决方案(UML、MBSE、SYSML、BPMN、体系结构设计、需求管理、DoDAF等)
模型驱动的研发管理一站式解决方案   Trufun QQ:344593239   咨询热线:3379288210  

设计模式之外观模式


时间: 2016-01-07    来源: 楚凡科技

 


        外观模式介绍

        结构型模式中一个比较常用的模式-外观模式,这个模式呢,有个最大的特点将细粒度的对象包装成粗粒度的对象,应用程序通过访问这个外观对象,来完成细粒度对象的调用,外观模式一般是分布式应用和系统架构中的应用服务层的设计中常用的方式,并且一般结合外观模式+DTO来完成服务层的设计,提供分布式应用服务的高效服务,外观模式我们可以这样理解,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性。本文将会从以下几个方面进行讲述:
       1、外观模式的使用场景和特点
       2、外观模式的实现方案。
       3、总结外观模式。
我们这里先给出一个外观模式的原理图:

         这是未使用外观模式之前的情况,下面给出使用外观模式后的情形:

         通过外观对象来组织细粒度的服务的调用,外观对象提供给外部应用程序可以使用的服务,而具体的调用细粒度的过程则被外观对象给封装起来,当然这个过程就是封装变化的部分,而将变化的部分与应用程序进行隔离,无疑对程序的易用性和可维护性都是很大的提高。

        外观模式的特点及使用场景

        外观模式的主要思想是将复杂的细粒度的对象服务包装成简单的易使用的粗粒度的功能服务,我们大家最容易理解和知道的外观模式就是,使用的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,提供远程服务调用的性能,这些都是好的设计方式。我们来看看简单的示例吧,我想我们更能了解其中的奥妙。看图说话吧,我们这里以一次远程同步服务为例。
        未使用外观模式前:

         一个简单的同步服务,由于应用程序在这次服务中为了完成同步操作,必须进行3次远程连接来进行把3个对象进行同步,那么如果我们使用外观对象之后呢,我们只需要访问一次即可完成3个对象的同步。这无疑提高了系统的性能和效率。并且通过DTO来进行传输的话,可以提供远程服务调用的访问此时和效率。

       外观模式的实现方案

      ▶ 外观模式的通信图

 



 

      ▶ 外观模式的经典实现

我们先来看看外观模式的经典实现,我们这里已二进制流序列化服务外观为例来说明下经典实现吧。
定义一个二进制序列化外观类:
    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);
        }
这样我们就提高了外观模式的灵活性。

      ▶ 外观模式的其他考虑

        上面给出的外观模式,我们在具体的测试代码中还是直接通过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);
        }
              传统外观模式的扩展
下面给我们来看看另外一种形式的外观模式中的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的形式访问,也可以通过客户端应用程序的形式时,可能通过外观模式来提供远程服务,让应用程序进行远程调用,这样通过外观形式提供服务,那么不管是什么样的客户端都访问一致的外观服务,那么以后就算是我们的应用服务发生变化,那么我们不需要修改没一个客户端应用的调用,只需要修改相应的外观应用即可。

 


分享到: