1. 程式人生 > >模式系列談之Facade模式

模式系列談之Facade模式

看到“門面”這個詞,大家一定都覺得很熟悉。不錯,這個詞正是借用了我們日常生活中的“門面”的概念。日常生活中的“門面”,正是我們買東西的地方。因此可以這麼說,“門面”就是這麼一個地方,它們跟各種商品的生產商打交道,收集商品後,再賣給我們。換句話說,如果沒有“門面”,我們將不得不直接跟各種各樣的生產商買商品;而有了“門面”,我們要買東西,直接跟“門面”打交道就可以了。

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();
……