目前整理的门面模式 的使用有三种情况。下面分别讨论。
第一种情况
客户类要使用的功能分布在多个类中,这些类可能相互之间没有什么关系;客户在使用后台的时候,必须先初始化要使用到的功能所在的类,然后才能使用。这时候,适合将这些功能集中在一个Façade类里,还可以替用户做一些初始化的工作,以减轻用户的负担。
例如,以商店为例。假如商店里出售三种商品:衣服、电脑和手机。这三种商品都是由各自的生产厂商卖出的,如下: public class CoatFactory { public Coat saleCoat() { …… return coat; } …… } 然后是电脑的厂家类: public class ComputerFactory { public Computer saleComputer() { …… return computer; } …… } 最后是手机商类: public class MobileFactory { public Mobile saleMobile() { …… return mobile; } …… } 如果没有商店,我们就不得不分别跟各自的生产商打交道,如下: //买衣服 CoatFactory coatFactory = new CoatFactory(); coatFactory.saleCoat(); //买电脑 ComputerFactory computerFactory = new ComputerFactory(); computerFactory.saleComputer(); //买手机 MobileFactory mobileFactory = new MobileFactory(); mobileFactory.saleMobile(); 对我们顾客来说,和这么多的厂家类打交道,这显然是够麻烦的。 这样,我们就需要创建一个商店类了,让商店类和这些厂家打交道,我们只和商店类打交道即可,如下: public class Store { public Coat saleCoat() { CoatFactory coatFactory = new CoatFactory(); return coatFactory.saleCoat(); } public Computer saleComputer() { ComputerFactory computerFactory = new ComputerFactory(); return computerFactory.saleComputer(); } public Mobile saleMobile() { MobileFactory mobileFactory = new MobileFactory(); return mobileFactory.saleMobile(); } } 好了,现在我们要买东西,不用去跟那么多的厂家类打交道了。 Store store =new Store(); //买衣服 store.saleCoat(); //买电脑 store.saleComputer(); //买手机 store.saleMobile(); 呵呵,这样对我们客户类来说,是不是简单多了。 第二种情况
客户要完成的某个功能,可能需要调用后台的多个类才能实现,这时候特别要使用Façade模式。不然,会给客户的调用带来很大的麻烦。请看下面的例子。 我经常看到后台编码人员,强迫它们的使用者写出如下的代码: …… String xmlString = null; int result = 0; try { xmlString = gdSizeChart.buildDataXML(incBean); String path = "D:/Eclipse3.0/workspace/PLMSuite/AppWeb/PM/productSpecification/gridfile.xml"; File f = new File(path); PrintWriter out = new PrintWriter(new FileWriter(f)); out.print(xmlString); out.close(); System.out.println("\r\n\r\n sumaryAction" + xmlString + "\r\n\r\n"); request.setAttribute("xmlString", xmlString); } catch(Exception ex) { ex.printStackTrace(); } 这段代码前面即省略号省略掉的一部分是客户类调用后台的一部分代码,是一个相对独立的功能。后面这一部分也是一个相对独立的功能,而后台代码设计人员却把这个功能留给客户类自己来实现。 我就很怀疑,让客户类做这么多事情,到底要你的后台做什么?你还不如直接把所有的事情都给客户类做了得了。因为,你后台做了一半,剩下的一部分给客户类做,客户类根本就不明白怎么回事,或者说他不清楚你的思路,这样做下去更加困难。可能这点逻辑对你来说,很简单。但使用者不明白你的思路啊,他不知道来龙去脉,怎么往下写? 如果在这里有一个Façade类,让它来做不该由客户类来做的事,是不是简单多了呢?如下是一个Façade类: public class Façade { public static void doAll(PE_MeasTableExdBean incBean, HttpServletRequest request) { …… request.setAttribute(“xmlString”,Façade.getFromOut(incBean)); } private static String getFromOut(PE_MeasTableExdBean incBean) { try { xmlString = gdSizeChart.buildDataXML(incBean); String path = "D:/Eclipse3.0/workspace/PLMSuite/AppWeb/PM/productSpecification/gridfile.xml"; File f = new File(path); PrintWriter out = new PrintWriter(new FileWriter(f)); out.print(xmlString); out.close(); System.out.println("\r\n\r\n sumaryAction" + xmlString + "\r\n\r\n"); return xmlString; } catch(Exception ex) { ex.printStackTrace(); return null; } } } 那么客户类的调用就是下面的样子: Façade.doAll(incBean,request); 这样,客户是不是轻松多了?值得注意的是,Façade类中的getFromOut方法其实不应该在Façade类中,本文为了简单起见而放在了这个类中,对Façade类来说是不符合单一职责原则的。 最后总结一下第二种情况的模式。后台为实现某一个功能有如下类: public class ClassA { public void doA() { …… } …… } public class ClassB { public void doB() { …… } …… } public class ClassC { public void doC() { …… } …… } 如果客户类需要这样调用: …… ClassA a = new ClassA(); a.doA(); ClassB b = new ClassB(); b.doB(); ClassC c = new ClassC(); c.doC(); …… 那么就适合做一个Façade类,来替客户类来完成上述的功能,如下: public class Façade { public void doAll() { ClassA a = new ClassA(); a.doA(); ClassB b = new ClassB(); b.doB(); ClassC c = new ClassC(); c.doC(); } } 则客户类的调用如下: …… Façade façade = new Façade(); façade.doAll(); …… 第三种情况 主要从安全性的角度考虑。
在 How Tomcat Works 的书中,第二章的例子。
底层调用servlet 的 server 方法时,如此调用:
servlet.service((ServletRequest) request, (ServletResponse) response);
Request、 Response 都是实现 ServletResponse 接口的对象。
Request、 Response 又都有自己独有的公共方法 供其他底层框架调用。
但是,我们知道,servlet.service( ) 方法实际上是由应用程序员来实现的。应用程序员可以在代码中强制类型转换 ServletRequeat ServletResponse 为 Request Response 对象,进而调用他们独有的方法,这样有安全隐患。
所以用到了门面模式: Servlet servlet = null; RequestFacade requestFacade = new RequestFacade(request); ResponseFacade responseFacade = new ResponseFacade(response); try { servlet = (Servlet) myClass.newInstance(); servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade); } catch(Exception e) { System.out.println(e.toString()); } public class RequestFacade implements ServletRequest { private ServletRequest request = null; public RequestFacade(Request request) { this.request = request; } }
在门面中,实现接口,保存一个对象指向Request对象。接口的方法就是简单的调用Request的方法。
这样,传给 servlet.service( ) 方法的实际上是两个门面。 Request 对象独有的方法不会出现在 门面对象中,这样就保护了 Request 对象。
