門面模式(Facade Pattern)
門面模式的定義:
門面模式也叫做面板模式,是一種常見的封裝模式,它的定義:要求一個子系統的外部與其內部的通訊必須通過一個統一的對象進行。門面模式提供一個高層次的介面,使子系統更容易使用。
門面模式的優點:
1. 減少系統的相互依賴。調用者通過門面角色提供的介面訪問子系統,這樣只要門面角色提供的介面不發生變化,子系統怎麼改變都不會影響高層代碼,因為門面角色對於調用者來說都是同一個門面。
2. 有較高的靈活性。依賴降低了,靈活性也就變高了。
3. 提高了安全性。門面角色可以限制外部能訪問到的子系統功能,只需要為需要的功能提供一個介面即可,沒有提供介面則就不能訪問。
門面模式的應用情境:
1. 需要為一個複雜的模組或子系統提供一個外界提供者。比如,需要使用一個系統的功能,需要調用50個函數,50個函數之間還需要有一定順序,總不能外界直接按順序要求調用這50個函數吧,要是更複雜呢(有5000個函數-_-!!),要真這樣一旦某一個函數出錯你也就再見了。而且這樣寫高層模組與子系統模組強耦合在一起了,一旦修改子系統,高層模組也需要變動。為瞭解決這種強耦合,我們可以使用門面角色將子系統封裝起來,然後為高層模組提供業務的介面,這樣高層模組就只依賴門面角色了,子系統的修改不會影響到高層模組。
2. 子系統相對獨立——外界只需要黑箱操作。
門面模式的通用類圖:
Facade(門面角色):該角色封裝了子系統,知曉子系統的功能和職責。該角色會將所有從用戶端發來的請求委派到相應的子系統去,它其實只是一個委託類,無實際的商務邏輯。高層模組通過使用該角色提供的介面訪問子介面。
SubSystem(子系統角色):可以是一個也可以是多個子系統。每個系統都是類集合。子系統不知道門面角色的存在,在其角度來看,門面角色其實就是個用戶端。
門面模式的通用代碼:
子系統角色:
/*子系統A*/
public class SubSystemA
{
public void logicA()
{
System.out.println("logicA");
}
}
/*子系統B*/
public class SubSystemB
{
public void logicB()
{
System.out.println("logicB");
}
}
/*子系統C*/
public class SubSystemC
{
public void logicC()
{
System.out.println("logicC");
}
}
門面角色:
public class FacadeRole
{
/*封裝的子系統集*/
private SubSystemA systemA = new SubSystemA();
private SubSystemB systemB = new SubSystemB();
private SubSystemC systemC = new SubSystemC();
//提供調用子系統A的功能的介面
public void logicA()
{
/*委託給系統A*/
systemA.logicA();
}
//提供調用子系統B的功能的介面
public void logicB()
{
/*委託給系統B*/
systemB.logicB();
}
//提供調用子系統B的功能的介面
public void logicC()
{
/*委託給系統C*/
systemC.logicC();
}
}
情境類:
public class Client
{
public static void main(String[] args)
{
/*建立門面角色*/
FacadeRole facadeRole = new FacadeRole();
/*訪問子系統A的功能*/
facadeRole.logicA();
/*訪問子系統B的功能*/
facadeRole.logicB();
/*訪問子系統C的功能*/
facadeRole.logicC();
}
}
門面模式的補充
補充一:子系統可以擁有多個門面
一般情況下,一個子系統只需要一個門面角色就足夠了,但是遇到如下情況需要考慮多門面角色:
1. 當一個門面角色比較龐大的時候,可以按功能分類拆分成多個較小的門面角色。
2. 需要為子系統提供不同的訪問路徑。比如,子系統對信任模組提供全功能訪問,而對不信任模組提供受限制的功能訪問,此時可以使多個門面模式,限制不同模組可以訪問的功能。
補充二:門面角色不應該參與子系統的商務邏輯
public class FacadeRole
{
/*封裝的子系統集*/
private SubSystemA systemA = new SubSystemA();
private SubSystemB systemB = new SubSystemB();
private SubSystemC systemC = new SubSystemC();
//提供調用子系統A的功能的介面
public void logicA()
{
/*委託給系統A*/
systemA.logicA();
}
//提供調用子系統B的功能的介面
public void logicB()
{
/*委託給系統B*/
systemB.logicB();
}
//提供調用子系統B的功能的介面
public void logicC()
{
/*委託給系統A*/
systemA.logicA();
/*委託給系統C*/
systemC.logicC();
}
}
logicC()方法修改如上,這樣的改動看上去沒什麼錯誤,其實是不正確的。門面模式的定義就是說門面角色是一個訪問子系統的統一的介面,它只是一個委託類,提供了訪問子系統的路徑而已,不應該參與具體的商務邏輯(這個修改已經參與了子系統的邏輯,必須先執行A系統的logicA然後在執行C系統的logicC)。如上的修改會導致一個非常嚴重的依賴:子系統必須依賴門面才能被正確的訪問。遇到多個子系統有依賴的情況,需要再對有依賴的子系統封裝起來。
public class ContextAC
{
private SubSystemA systemA = new SubSystemA();
private SubSystemC systemC = new SubSystemC();
public void execute()
{
systemA.logicA();
systemC.logicC();
}
}
Facade角色:
public class FacadeRole
{
/*封裝的子系統集*/
private SubSystemA systemA = new SubSystemA();
private SubSystemB systemB = new SubSystemB();
private ContextAC contextAC = new ContextAC();
//提供調用子系統A的功能的介面
public void logicA()
{
/*委託給系統A*/
systemA.logicA();
}
//提供調用子系統B的功能的介面
public void logicB()
{
/*委託給系統B*/
systemB.logicB();
}
//提供調用子系統B的功能的介面
public void logicC()
{
contextAC.execute();
}
}
這樣封裝後,Facade角色又與具體業務無關了。這樣子系統怎麼改變,門面模式依然不變。