上篇我们主要讲述了创建型模式中的最后一个模式-原型模式,我们主要讲述了原型模式的几类实现方案,和原型模式的应用的场景和特点,原型模式适合在哪些场景下使用呢?我们先来回顾一下我们上篇讲述的3个常用的场景。
1、我们在运行态的时候,动态的创建一个动态类型的对象的时候,可能我们使用原型模式,可以动态的创建指定类型的副本,这无疑是好的选择,否 则如果通过我们前面讲述的几个创建型模式来实现的话,效率和代价上是非常大的。
2、有的时候我们需要对比一个对象在处理前和处理后进行对象状态的对比,对比是否处理后对象的状态是否发生变化,或者是其他的要求。这个时候通过原型模式来克隆对象的副本,远比通过引入其他的Factory或者abstract Factory 来的有效和更容易实现。
3、如果我们发现有一类这样的对象,这类对象通常来说比较简单,并且这类对象之间的差别很有规律,并且这类对象数量一般有限,那么这个时候,我们通过原型模式来做的话,通过一个对象来复制创建其他类型的对象可能比通过引入其他的Factory或者abstract Factory 更容易实现,而且只需要对象本身提供一个Clone()方法即可。
4、有的时候我们的项目中有这样的情况,我们是在别人的功能的基础上进行扩展,我们有不能修改现有的程序,如果这个应用程序是基于其他类型的创建型模式,那么如果我们在系统中新增一个类型的时候,我们需要修改统一的创建型模式中的代码,不管是修改配置文件还是具体的功能代码,无疑都是要修改的,那么如果我们通过原型模式的话,只需要在新增类型的对象内部,提供一个克隆方法即可,完成新对象的创建。
通过上面的情况,那么我们也能大概看出来原型模式的有一个前提,就是必须是基于对象之上调用Clone()方法完成对象的复制,如果没有创建这个对象的话,可能不能直接使用该方法。
我们也讲述了,对于Clone()对象的时候,深复制和浅复制的情况,还包括通过序列化对象的形式来完成对象的深复制。
本文主要是讲述结构型模式中一个比较常用的模式-外观模式,这个模式呢,有个最大的特点将细粒度的对象包装成粗粒度的对象,应用程序通访问这个外观对象,来完成细粒度对象的调用,外观模式一般是分布式应用和系统架构中的应用服务层的设计中常用的方式,并且一般结合外观模式+DTO来完成服务层的设计,提供分布式应用服务的高效服务,外观模式我们可以这样理解,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性。本文将会从以下几个方面进行讲述:
1、外观模式的使用场景和特点
2、外观模式的实现方案。
3、总结外观模式。
我们这里先给出一个外观模式的原理图:
这是未使用外观模式之前的情况,下面给出使用外观模式后的情形:
通过外观对象来组织细粒度的服务的调用,外观对象提供给外部应用程序以使用的服务,而具体的调用细粒度的过程则被外观对象给封装起来,当然这个过程就是封装变化的部分,而将变化的部分与应用程序进行隔离,无疑对程
序的易用性和可维护性都是很大的提高。
a、上篇回顾。
b、摘要。
c、本文大纲。
d、外观模式的特点及使用场景。
e、外观模式的实现方案。
f、外观模式使用总结。
外观模式的主要思想是将复杂的细粒度的对象服务包装成简单的易使用的粗粒度的功能服务,我们大家最容易理解和知道的外观模式就是,使用的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来进行传输的话,可以提供远程服务调用的访问此时和效率。
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()的方式来使用。我们可以改进一下上面的外观模式,通过定义接口,来解耦客户端程序的调用,通过提供一个接口,客户调用通过接口的形式来调用,无疑就可以解决这样依赖关系:
我们先来看看接口的定义形式:
///
/// 序列化服务的外观接口
///
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.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的形式:
///
/// DTO(数据传输对象),也可以称之为数据载体
///
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对象的外观类代码如下:
///
/// 远程访问服务
///
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