门面模式
门面模式定义:要求一个子系统的外部与该子系统的内部通信必须通过一个统一的对象进行.
举个栗子:我们在京东上网购商品,这是就需要一个Custormer类,并为该类定义一个Shopping()方法。假设京东只有三个部门:收银部,仓储部和物流部.
用户购物需要这三个部门协调才能完成。
代码清单-1 收银部
public class PaymentImpl implements Payment { public String pay(double price) { String book = "书籍,《明朝那些事》"; System.out.println("您已付款:" + price + "元,购买的是:" + book); return book; } }
代码清单-2 仓储部
/** * 仓储服务 */ public class StorageImpl implements Storage{ /** * 打包 * @param ware 商品 * @return */ public String pack(String ware) { System.out.println("仓库人员正在打包:" + ware); return null; } }
代码清单-3 物流部
/** * 物流服务 */ public class TransportImpl implements Transport{ /** * 快递 * @param ware 商品 * @return */ public String deliver(String ware) { System.out.println("商品正在飞奔您怀中的路上:"+ware); return null; } }
代码清单-4 Custormer
public class Customer { //购物 public void shopping(){ //依次创建三个部门 Payment payment = new PaymentImpl(); Storage storage = new StorageImpl(); Transport transport = new TransportImpl(); //支付 String ware = payment.pay(28.0); //打包 storage.pack(ware); //快递运输 transport.deliver(ware); } }
分析:正如上面的代码所示,Customer需要依次调用三个部门的方法才能完成这个shopping方法,实际上,如果这个购物流程涉及更多的部门,那么程序就需要调用更多部门的方法来实现这个shopping方法——这就会增加shopping方法的实现难度了。
为了解决这个问题,我们可以为Payment、Storage、Transport三个部门提供一个门面(Facade),使用Facade来包装这些类,对外提供一个简单的访问方法。
代码清单-5 门面类
public class Facade { private Payment payment; private Storage storage; private Transport transport; public Facade(Payment payment, Storage storage, Transport transport) { this.payment = payment; this.storage = storage; this.transport = transport; } public void fastShopping(){ //支付 String ware = payment.pay(28.0); //打包 storage.pack(ware); //快递运输 transport.deliver(ware); } }
那Customer的shopping方法就可以改为
public class Customer { private Facade facade = null; public Customer(Facade facade) { this.facade = facade; } //购物 public void shopping(){ facade.fastShopping(); } }
从上面的结构可以看出,如果不采用门面模式,客户端需要自行决定需要调用哪些类、哪些方法,并需要按合理的顺序来调用它们才能实现所需的功能。不采用门面模式时,程序有如图所示的结构:
然而,使用了门面模式之后,客户端代码只需要和门面类进行交互即可,于是,程序结构图变成了如下样式:
其实,Spring的HibernateTemplate类就是使用的门面模式:当我们的程序使用Hibernate的find()方法时,程序只要一行代码即可得到查询返回的List。但实际上该find()方法后隐藏了如下代码:
Session session = sf.openSession(); Query query = session.createQuery(hql); for(int i=0; i<args.length; i++) { query.setParameter(i, args[i]); } query.list();
因此我们可以认为HibernateTemplate是SessionFactory,Session、Query等类的门面。当客户端程序需要进行持久化查询时,程序无需调用这些类,而是直接调用HibernateTemplate门面类中的相印方法即可。
除此之外,JavaEE应用里使用业务逻辑组件来封装DAO组件也是典型的门面模式——每个业务逻辑组件(一般是Service层)都是众多DAO组件的门面,系统的控制器类无需直接访问DAO组件,而是由业务逻辑方法来组合多个DAO方法以完成所需功能。而Action只需与业务逻辑组件交互即可。