在分布式体系结构中使用Fa#231;ade设计模式的方法

在分布式体系结构中使用Fa#231;ade设计模式的方法

尽管大家可能对于Façade设计模式早已耳熟能详,但是为了使这篇文章内容显得完整,我们还是先来简单回顾一下什么是Façade模式。

所谓Façade模式,就是要为子系统中一组接口(方法)提供一个一致的界面。Façade模式定义了一个高层接口(方法),这个接口使得这一子系统更加容易使用。这样做的好处就是将一个系统划分为若干个子系统有利于降低系统的复杂性,并使子系统之间的通信和相互依赖关系达到最校如图1所示。

在分布式体系结构中使用Fa#231;ade设计模式的方法

图1 外观模式结构视图

在分布式应用程序开发中,一个Façade是介于服务提供者模块和客户端之间的层,这里服务提供者封装了具体的服务,而Façade则负责按照一定的业务逻辑或者职责对服务进行封装,构成更高一层的任务。比如在一个订票系统中,服务提供者要处理客户信息和订票信息,两个不同的操作组合在一起就形成了订票处理这样一个外观操作。如图2所示。

在分布式体系结构中使用Fa#231;ade设计模式的方法

图2 应用Façade的分布式体系结构示意图

显然,如果不采用Façade模式,客户端需要先调用服务端的登录服务进行用户的身份验证,然后再分别调用远程服务提供者提供的AddCustomer和AddBooking两个方法,这样必然要进行多次数据库操作,事实上对每一个操作建立新的连接效率非常低下,而且这是不可避免的。与此同时,由于与服务端进行多次交互,网络调用的开销也很可观。

那么使用Façade以后会是怎样的状况呢,先看以下的代码:

public class BookingFacade

{

//CustomerDetails与BookingDetails类对应客户和订票信息实体类

public void ProcessBooking(CustomerDetails customer,BookingDetails booking)

{

//CustomerOperation与BookingOperation对应客户和订票信息的具体操作,如增、删、改、查等(CRUD)

if(IsValidate(customer))

{

CustomerOperation cOper = new CustomerOperation();

BookingOperation bOper = new BookingOperation();

If(!cOper.Contains(customer))

{

cOper.AddCustomer(customer);

}

bOper.AddBookong(booking);

}

}

}

由于使用了Façade,客户端只需要调用调用一次远程对象的ProcessBooking方法就可以完成原本要分几步的操作,这样也就从客户端去掉了相当一部分业务逻辑,并且有效的减少了网络调用(只有一个而不是两个或三个)。由此带来的负面效应是,在设计时要考虑是否还要将登录验证、添加用户和订票信息这些方法公开出去,如果不公开,那么客户端只能使用已有的、固定的外观中的方法,这样在某种程度上必然缺乏灵活性。

与此同时,在使用了Façade后,虽然将几个操作封装了起来,但是目前为止他们仍然还要为每个数据库操作创建一个新的连接。既然Façade将操作集中在一起,理所当然的也可以将这几个数据库连接合并为一个统一的进行处理。这个时候我们需要创建一个专用的数据库连接助手类,它能够在Façade方法开始时提供并开放连接对象,在结束时关闭连接对象,并将连接对象传递给服务提供者使用。为了保证业务Façade不会与具体数据库类型产生紧耦合,连接对象应作为基本的System.Object类型返回。代码如下:

public class BookingFacade

{

public void ProcessBooking(CustomerDetails customer,BookingDetails booking)

{

if(IsValidate(customer))

{

//通过一个连接辅助类提供数据库连接对象

DBHelper helper = new DBHelper();

Object conn = helper.GetConnection();

CustomerOperation cOper = new CustomerOperation(conn);

BookingOperation bOper = new BookingOperation(conn);

If(!cOper.Contains(customer))

{

cOper.AddCustomer(customer);

}

bOper.AddBookong(booking);

//确保连接对象被释放

helper.ReleaseConnection(conn);

}

}

}

Façade由于处于包装的顶层,很容易替换内部的数据操作形式而不必改变客户端代码,为了提高系统的可扩展性和灵活性我们总是期望这样一种理想的状态。但是,实际情况往往不尽人意,客户端总是或多或少的绑定某些特定的数据表示,比如如果客户对象的信息发生更新,那么服务端和客户端的CustomerDetails类的版本甚至相应数据库表都要更新,同时客户端界面上也要发生一些改动以反映出这种更新。此时,衡量我们设计好坏的标准就变成刚才提到在客户端特定数据绑定的多少问题以及改动程度的轻重问题。

更深入一步,到此为止我们只是提到在服务端使用Façade,但并没有说明这个Façade包装的是一个Web服务还是一个Remoting,事实上这无关紧要。因为无论是哪种形式,客户端要调用的仍旧是这个Façade对象公开的方法,从而屏蔽了对其封装了的那些底层对象方法的调用,也就是隔离了底层的差别。讲到这里,不能不提一下SOA(面向服务的架构),很多人误认为系统中使用了传统的Web服务或者Remoting就符合SOA了,实际上是错误的,或者说是不确切的。因为至少有一个事实可以证明这一点,那就是SOA对外部提供的服务是有粒度要求的(应该是粗粒度的),难道仅仅对外提供了一个登录服务就能称其为SOA架构吗,这显然不是SOA的初衷。从某种角度而言,传统的Web服务和Remoting提供的服务仅仅是SOA提供服务的构成元素,也就是说我们能够将前者转变为SOA中所谓的服务,实际上这里提到的Façade正是这一转变的关键所在。

最后,再来讨论一下Façade和事务处理。也许很多人因为要使用合适的分布式事务处理煞费苦心,也因为MSDTC的种种问题而气急败坏,但是如果将各种数据库相关的操作集中在一起在服务器端使用数据库本身的事务机制则是非常有效且简便的解决方法,Façade正好成全了这样的构想。

我们可以用多种方式给Façade增加事务支持,一种方式就是在刚才的数据库连接辅助类中增加一个方法:GetTransaction()返回一个事务对象,它将控制Façade操作的全过程。代码如下:

public class BookingFacade

{

public void ProcessBooking(CustomerDetails customer,BookingDetails booking)

{

if(IsValidate(customer))

{

DBHelper helper = new DBHelper();

Object conn = helper.GetConnection();

Object transaction = conn.GetTransaction();

//开始事务

transaction.Begin();

try

{

CustomerOperation cOper = new CustomerOperation(conn);

BookingOperation bOper = new BookingOperation(conn);

If(!cOper.Contains(customer))

{

cOper.AddCustomer(customer);

}

bOper.AddBookong(booking);

}

catch(Exception ex)

{

//TODO这里应捕获特定异常

transaction.Rollback(); //事务回滚

}

finally

{

//提交事务

transaction.Commit();

//关闭连接

helper.ReleaseConnection(conn);

}

}

}

}

另外一个方法就是使用COM+的事务处理,由于它使用了两阶段的处理过程,因此开销大一些。需要指出的是,当在Web服务中实现Façade的事务控制是非常简单容易的,因为此时可以使用<WebMethod>特性中(Attribute)的Transaction属性。事务处理将自动进行,除非有异常引发。示意代码如下:

public class BookingFacade

{

<WebMethod(false,TransactionOption=TransactionOption.RequiresNew)>

public void ProcessBooking(CustomerDetails customer,BookingDetails book)

{

......

}

}