代碼的“門面”——模式系列談之Façade模式

代碼的“門面”
                                                          ——模式系列談之Façade模式
 
 
看到“門面”這個詞,大家一定都覺得很熟悉。不錯,這個詞正是借用了我們日常生活中的“門面”的概念。日常生活中的“門面”,正是我們買東西的地方。因此可以這麼說,“門面”就是這麼一個地方,它們跟各種商品的生產商打交道,收集商品後,再賣給我們。換句話說,如果沒有“門面”,我們將不得不直接跟各種各樣的生產商買商品;而有了“門面”,我們要買東西,直接跟“門面”打交道就可以了。
Façade模式正是這樣一個“門面”:我們本來需要與後臺的多個類或者接口打交道,而Façade模式是客戶端和後臺之間插入一箇中間層——門面,這個門面跟後臺的多個類或接口打交道,而客戶端只需要跟門面打交道即可。
使用Façade模式可以說是後臺設計和編碼人員的一個必備素質。我不止碰到過一個這樣的後臺開發人員,他們認爲只要把後臺功能完成了就萬事大吉,而沒有站在後臺使用者的角度來看一看自己寫出來的代碼。其實,我們寫出來的後臺代碼是要給別人使用的,所以我們提供給使用者的接口要越簡單越好,這不單是對使用者好,同時對開發者也是好處多多的,至少你的接口簡單了,你和使用者的交流就容易了。
而Façade模式中的Façade類正是這樣一個用戶接口,它和後臺中的多個類產生依賴關係,而後臺的客戶類則只跟Façade類產生依賴關係。爲什麼要這麼做?其中的原因十分簡單:後臺的開發者熟悉他自己開發的各個類,也就容易解決和多個類的依賴關係,而後臺的使用者則不太熟悉後臺的各個類,不容易處理和它們之間的依賴;因此,後臺的開發者自己在Façade類中解決了與後臺多個類之間的依賴,後臺的使用者只需要處理和Façade類的依賴即可。
好了,閒話少說。我們下面就以幾個具體的例子來看一看Façade模式是怎麼使用的。實際編程中,能使用到Façade模式的情況有很多,以下就分兩種情況來具體說一說Façade模式的使用。可能還會有其他的情況,大家在實踐中也可以加以補充。
第一種情況,客戶類要使用的功能分佈在多個類中,這些類可能相互之間沒有什麼關係;客戶在使用後臺的時候,必須先初始化要使用到的功能所在的類,然後才能使用。這時候,適合將這些功能集中在一個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();
……
 
發佈了93 篇原創文章 · 獲贊 0 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章