外观模式

前一段时间看how tomcat works这本书,发现里面介绍到了Facade(外观、门面)设计模式的使用,在里面,tomcat中通过利用了RequestFacade接口对Request对象的访问进行了安全性控制。我处于好奇,去查了查head first设计模式和GOF设计模式对于这个外观模式的介绍,然后又看了看代理模式,个人感觉,tomcat使用的更像是代理模式,而并非传统意义上的外观模式。

原因如下:

1.GOF中定义外观模式:为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接
口使得这一子系统更加容易使用。ps:这说明外观模式通常是提供一个很简单的接口,调用一系列底层的复杂接口,用来简化客户端的调用,让系统结构对于客户端更加透明。

2.GOF中定义代理模式:为其他对象提供一种代理以控制对这个对象的访问。ps:就像tomcat中顾虑的一样,我不能让客户直接就能拿到Request对象,这样,他会搞的乱七八糟,于是我提供了一个接口,用来代理这个对象,控制它哪些可以被访问,哪些不行。

不过,也有可能我自己理解不到位,其实tomcat用的是外观而我没看出来罢了,。不过先不纠结这些了,我通过对head first设计模式的阅读,以及对gof设计模式的查阅。总结了一下外观模式真正合适的使用场景,希望大家如果有不同意见也能多多给出来,我这边也能学习到。

设想你平时在购物的时候,必然都会有个下单操作,这个操作其实就是对你已经生成的订单进行后续处理,处理的流程比较复杂,因为会涉及到很多流程。这里我就比作有修改订单状态的流程、通知物流中心发货的流程、计费流程、处理结果通知客户的流程。如果把这一系列流程都提供给客户,让客户自行调用的话,客户要自行组织先后关系,会很麻烦,而且也暴露的自己的业务逻辑实现细节,不太安全。这时,我们传说中的外观模式就派上用场了。。。。

来,回顾一下他的定义:为子系统中的一组接口提供一个一致的界面, 外观模式定义了一个高层接口,这个接
口使得这一子系统更加容易使用

这里,我们的子系统就是修改订单状态的流程、通知物流中心发货的流程、计费流程、处理结果通知客户的流程等一系列流程。我们如果要客户使用起来方便,就要提供一个高层接口,而这个高层接口,我们就简单的定义为下单接口。

package facade;

/**
 * 
* @ClassName: Client 
* @Description: 客户访问的接口 
* 
* 注明:这里的接口是广义的,并不专指传统意义上java的interface
* 我这里指代的是整个提供了某个或者某些功能的Method、class、interface
* 
* @author minjun
*
 */
public class Client {
	
	/**订单接口,通常都是单例实现*/
	private static final OrderService orderService=new OrderService();
	
	public static void main(String[] args) {
		//获取订单编号
		int orderId=getOrderIdFromRequest();
		//执行下单操作
		String msg=orderService.order(orderId);
		//在jsp页面展现下单结果
		showInJsp(msg);
	}

	private static void showInJsp(String msg) {
		System.out.println(msg);
	}

	private static int getOrderIdFromRequest() {
		System.out.println("从客户端发送过来的订单编号为"+7);
		return 7;
	}
}



如上述代码,如果客户能够简单的通过这个orderService的order(下单接口)执行操作,那么会是多么方便啊。这就是我们的目的,提供一个简单的下单接口,具体里面怎么实现的,我们下面再来看(根据TDD测试驱动开发的指导原则)。

package facade;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
* @ClassName: OrderService 
* @Description: 订单接口 
* @author minjun
* @date 2015年5月3日 下午2:49:16 
*
 */
public class OrderService {
	
	/**单线程的线程池*/
	private static final ExecutorService exec=Executors.newSingleThreadExecutor();
	
	/**账户服务*/
	private static final AccountService accountService=new AccountService();
	
	/**物流服务*/
	private static final LogisticsServcie logisticsService=new LogisticsServcie(); 
	
	/**由于下单操作会涉及到一系列复杂的流程,比如修改订单状态、走支付接口扣费、通知物流系统,所以利用外观模式,提供一个简单的外部接口,让客户访问*/
	public String order(final int orderId) {
		//检查订单的合法性
		checkOrder(orderId);
		
		//异步调用操作,下单操作的一些列子流程通常很耗时,一般是放到队列里面执行,这里图方便,利用一个单线程异步处理
		exec.execute(new Runnable() {
			
			@Override
			public void run() {
				try {
					System.out.println("-----------------order start----------------------");
					//下单的一系列流程都会是一个原子操作,包含在一个事物中,同时成功或者同时失败
					
					//简单模拟开启事物
					Tx.openTransaction();
					//修改订单状态
					updateOrderStatus(orderId);
					
					//通知物流系统
					logisticsService.doNotify(orderId);
					
					//暂时性扣费操作,将客户的钱转移到一个中间平台--例如支付宝,等顾客验收货品的时候真正扣费
					accountService.settle(orderId);
					
					//将处理结果通知用户
					callbackUser(orderId);
					
					//提交事务
					Tx.commit();
					
					System.out.println("-----------------order end----------------------");
					
				} catch (Exception e) {
					Tx.callback();
				}
				
			}
		});
		return "下单成功";
	}

	protected void callbackUser(int orderId) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("获取用户信息");
		
		System.out.println("提示用户下单流程成功,已经发货并暂时性扣费,以短信的形式发送给客户");
	}

	protected void updateOrderStatus(int orderId) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("将"+orderId+"订单状态修改为已完成");
	}

	private void checkOrder(int orderId) {
		if(orderId<0){
			throw new RuntimeException("下单失败,订单不合法");
		}
		System.out.println("检查订单完成,合法订单");
	}

}



如上面代码所示,我将复杂的子流程全部扔到了OrderService接口中,通过order方法自行组织这里面的操作顺序。这样,我成功屏蔽了客户对于我流程细节的了解,并且提供了一个用起来更方便的接口让他调用,何乐而不为呢?

下面展示流程的执行结果:

从客户端发送过来的订单编号为7
检查订单完成,合法订单
下单成功
-----------------order start----------------------
开启事物
将7订单状态修改为已完成
通知顺丰处理物流
对用户1的订单7完成扣费操作,合作银行[农业银行]
获取用户信息
提示用户下单流程成功,已经发货并暂时性扣费,以短信的形式发送给客户
提交事务
-----------------order end----------------------



备注:这里我并没有使用类图给大家做演示,是因为虽然大多数人认为类图才是精华,但是我不这么认为,其实定义部分(意图)才是真正的精华,虽然简短,但是很明确。类图反正我是一下子看不出来是什么意思的。

总结:

外观模式

  1. 意图:为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  2. 动机:将一个系统划分成为若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。达到该目标的途径之一是就是引入一个外观Facade)对象,它为子系统中较一般的设施提供了一个单一而简单的界面。
  3. 适用场景:

  • 当你要为一个复杂子系统提供一个简单接口时
  • 客户程序与抽象类的实现部分之间存在着很大的依赖性
  • 当你需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。


你可能感兴趣的:(外观模式,GOF、设计模式)