看到“門面”這個詞,大家一定都覺得很熟悉。不錯,這個詞正是借用了我們日常生活中的“門面”的概念。日常生活中的“門面”,正是我們買東西的地方。是以可以這麼說,“門面”就是這麼一個地方,它們跟各種商品的生産商打交道,收集商品後,再賣給我們。換句話說,如果沒有“門面”,我們将不得不直接跟各種各樣的生産商買商品;而有了“門面”,我們要買東西,直接跟“門面”打交道就可以了。
Facade模式正是這樣一個“門面”:我們本來需要與背景的多個類或者接口打交道,而Facade模式是用戶端和背景之間插入一個中間層——門面,這個門面跟背景的多個類或接口打交道,而用戶端隻需要跟門面打交道即可。
使用Facade模式可以說是背景設計和編碼人員的一個必備素質。我不止碰到過一個這樣的背景開發人員,他們認為隻要把背景功能完成了就萬事大吉,而沒有站在背景使用者的角度來看一看自己寫出來的代碼。其實,我們寫出來的背景代碼是要給别人使用的,是以我們提供給使用者的接口要越簡單越好,這不單是對使用者好,同時對開發者也是好處多多的,至少你的接口簡單了,你和使用者的交流就容易了。
而Facade模式中的Facade類正是這樣一個使用者接口,它和背景中的多個類産生依賴關系,而背景的客戶類則隻跟Facade類産生依賴關系。為什麼要這麼做?其中的原因十分簡單:背景的開發者熟悉他自己開發的各個類,也就容易解決和多個類的依賴關系,而背景的使用者則不太熟悉背景的各個類,不容易處理和它們之間的依賴;是以,背景的開發者自己在Facade類中解決了與背景多個類之間的依賴,背景的使用者隻需要處理和Facade類的依賴即可。
好了,閑話少說。我們下面就以幾個具體的例子來看一看Facade模式是怎麼使用的。實際程式設計中,能使用到Facade模式的情況有很多,以下就分兩種情況來具體說一說Facade模式的使用。可能還會有其他的情況,大家在實踐中也可以加以補充。
第一種情況,客戶類要使用的功能分布在多個類中,這些類可能互相之間沒有什麼關系;客戶在使用背景的時候,必須先初始化要使用到的功能所在的類,然後才能使用。這時候,适合将這些功能集中在一個Facade類裡,還可以替使用者做一些初始化的工作,以減輕使用者的負擔。
例如,以商店為例。假如商店裡出售三種商品:衣服、電腦和手機。這三種商品都是由各自的生産廠商賣出的,如下:
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();
呵呵,這樣對我們客戶類來說,是不是簡單多了。
第二種情況客戶要完成的某個功能,可能需要調用背景的多個類才能實作,這時候特别要使用Facade模式。不然,會給客戶的調用帶來很大的麻煩。請看下面的例子。
我經常看到背景編碼人員,強迫它們的使用者寫出如下的代碼:
……
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();
}
這段代碼前面即省略号省略掉的一部分是客戶類調用背景的一部分代碼,是一個相對獨立的功能。後面這一部分也是一個相對獨立的功能,而背景代碼設計人員卻把這個功能留給客戶類自己來實作。
我就很懷疑,讓客戶類做這麼多事情,到底要你的背景做什麼?你還不如直接把所有的事情都給客戶類做了得了。因為,你背景做了一半,剩下的一部分給客戶類做,客戶類根本就不明白怎麼回事,或者說他不清楚你的思路,這樣做下去更加困難。可能這點邏輯對你來說,很簡單。但使用者不明白你的思路啊,他不知道來龍去脈,怎麼往下寫?
如果在這裡有一個Facade類,讓它來做不該由客戶類來做的事,是不是簡單多了呢?如下是一個Facade類:
public class Facade
{
public static void doAll(PE_MeasTableExdBean incBean, HttpServletRequest request)
{
……
request.setAttribute(“xmlString”,Facade.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;
}
}
}
那麼客戶類的調用就是下面的樣子:
Facade.doAll(incBean,request);
這樣,客戶是不是輕松多了?值得注意的是,Facade類中的getFromOut方法其實不應該在Facade類中,本文為了簡單起見而放在了這個類中,對Facade類來說是不符合單一職責原則的。
最後總結一下第二種情況的模式。背景為實作某一個功能有如下類:
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();
……
那麼就适合做一個Facade類,來替客戶類來完成上述的功能,如下:
public class Facade
{
public void doAll()
{
ClassA a = new ClassA();
a.doA();
ClassB b = new ClassB();
b.doB();
ClassC c = new ClassC();
c.doC();
}
}
則客戶類的調用如下:
……
Facade Facade = new Facade();
Facade.doAll();
……