一文搞懂設計模式—門面模式
軟件開發過程中,我們經常會遇到復雜系統,其中包含多個子系統和接口。在這種情況下,為了簡化客戶端的調用過程,提高代碼的可維護性和可讀性,我們可以使用門面模式。
門面模式(Facade Pattern)也叫做外觀模式,是一種結構型設計模式。它提供一個統一的接口,封裝了一個或多個子系統的復雜功能,并向客戶端提供一個簡單的調用方式。通過引入門面,客戶端無需直接與子系統交互,而只需要通過門面來與子系統進行通信。
門面模式中包含以下角色:
- 門面(Facade):門面角色是門面模式的核心,它封裝了系統內部復雜子系統的接口,為客戶端提供一個簡單的高層接口。門面角色知道哪些子系統負責處理請求,并將請求轉發給相應的子系統進行處理。
- 子系統(Subsystem):子系統角色是實際執行系統功能的組件。每個子系統都有自己的職責和行為,通過門面角色對外提供服務。
- 客戶端(Client):客戶端角色通過調用門面角色提供的高層接口來使用系統功能,而無需直接與子系統交互。
在門面模式中,門面角色充當了客戶端和子系統之間的中介者,隱藏了子系統的復雜性,簡化了客戶端的調用過程。客戶端只需要與門面角色進行交互,而不需要了解和處理子系統的具體細節。
注意:門面對象只是提供一個訪問子系統的一個路徑而已,它不應該也不能參與具體的業務邏輯,否則就會產生一個倒依賴的問題:子系統必須依賴門面才能被訪問,這是設計上一個嚴重錯誤,不僅違反了單一職責原則,同時也破壞了系統的封裝性。
使用場景
門面模式適用于以下情況:
- 當一個系統有很多復雜的子系統時,可以使用門面模式將其封裝起來,隱藏內部復雜性,簡化客戶端的調用。
- 當需要將客戶端與復雜的子系統解耦,降低系統之間的依賴時,可以使用門面模式。
以下是一個簡單的示例,展示了門面模式在電子商務系統中的應用。
假設我們的電子商務系統包含了訂單管理、庫存管理和支付管理等子系統。為了簡化客戶端的調用過程,我們可以使用門面模式來封裝這些子系統,并提供一個統一的接口。
// 訂單管理子系統
class OrderService {
public void createOrder() {
// 創建訂單的具體實現
}
}
// 庫存管理子系統
class InventoryService {
public void checkStock() {
// 檢查庫存的具體實現
}
}
// 支付管理子系統
class PaymentService {
public void makePayment() {
// 支付的具體實現
}
}
// 電子商務門面類
class ECommerceFacade {
private OrderService orderService;
private InventoryService inventoryService;
private PaymentService paymentService;
public ECommerceFacade() {
orderService = new OrderService();
inventoryService = new InventoryService();
paymentService = new PaymentService();
}
// 提供給客戶端的接口
public void placeOrder() {
orderService.createOrder();
inventoryService.checkStock();
paymentService.makePayment();
}
}在上述示例中,我們創建了一個電子商務門面類(ECommerceFacade),它封裝了訂單管理、庫存管理和支付管理等子系統,并提供了一個簡單的接口(placeOrder)供客戶端調用。這樣,客戶端只需要通過門面類來完成下單操作,而無需直接與子系統交互。
門面模式實現
下面是門面模式的基本結構:
// 子系統A
public class SubSystemA {
public void operationA() {
System.out.println("子系統A的操作");
}
}
// 子系統B
public class SubSystemB {
public void operationB() {
System.out.println("子系統B的操作");
}
}
// 子系統C
public class SubSystemC {
public void operationC() {
System.out.println("子系統C的操作");
}
}
// 門面類
public class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
private SubSystemC subSystemC;
public Facade() {
subSystemA = new SubSystemA();
subSystemB = new SubSystemB();
subSystemC = new SubSystemC();
}
// 提供簡單的接口給客戶端調用,隱藏了子系統的復雜性
public void operation() {
subSystemA.operationA();
subSystemB.operationB();
subSystemC.operationC();
}
}在上述代碼中,我們有三個子系統(SubSystemA、SubSystemB、SubSystemC),它們分別實現了具體的功能。然后,我們創建了一個門面類(Facade)來封裝這些子系統,并提供了一個簡單的接口供客戶端調用。
優缺點
優點
- 簡化客戶端的調用過程,隱藏了子系統的復雜性,提供了一個統一的接口,客戶端無需了解子系統的具體實現。
- 減少系統的相互依賴,解耦了客戶端與子系統之間的依賴關系。
- 提高了代碼的可維護性和可讀性。
缺點
- 門面模式可能會導致門面類變得龐大,承擔過多的責任。
- 如果需要修改子系統的功能,可能需要修改門面類。
門面模式優化
在實際應用中,我們可以對門面模式進行一些優化和擴展。以下是幾個常見的優化實現方式:
子系統解耦
門面類可以通過委托來調用子系統的功能,而不是直接依賴于具體的子系統。這樣可以使得子系統能夠獨立演化,不受門面類的影響。
// 門面類
class Facade {
private SubSystemInterface subSystemA;
private SubSystemInterface subSystemB;
public Facade() {
subSystemA = new ConcreteSubSystemA();
subSystemB = new ConcreteSubSystemB();
}
// 提供給客戶端的接口
public void operation() {
subSystemA.operation();
subSystemB.operation();
}
}
// 子系統接口
interface SubSystemInterface {
void operation();
}
// 具體的子系統A
class ConcreteSubSystemA implements SubSystemInterface {
public void operation() {
// 實現具體的功能
}
}
// 具體的子系統B
class ConcreteSubSystemB implements SubSystemInterface {
public void operation() {
// 實現具體的功能
}
}多個門面類
當門面已經龐大到不能忍受的程度,承擔過多的責任時,可以考慮使用多個門面類,每個門面類負責與特定的子系統交互,原則上建議按照功能拆分,比如一個數據庫操作的門面可以拆分為查詢門面、刪除門面、更新門面等。
// 子系統A的門面類
class SubSystemAFacade {
private SubSystemA subSystemA;
public SubSystemAFacade() {
subSystemA = new SubSystemA();
}
// 提供給客戶端的接口
public void operation() {
subSystemA.operationA();
}
}
// 子系統B的門面類
class SubSystemBFacade {
private SubSystemB subSystemB;
public SubSystemBFacade() {
subSystemB = new SubSystemB();
}
// 提供給客戶端的接口
public void operation() {
subSystemB.operationB();
}
}通過上述優化實現方式,我們能夠靈活地應對不同的需求和場景,提高了系統的可擴展性和維護性。
門面嵌套
假設我們有一個文件處理系統,其中包括三個子系統:文件讀取(FileReader)、文件寫入(FileWriter)和文件壓縮(FileCompressor)。
現在有兩個模塊來訪問該子系統:通用模塊(GeneralModule)可以完整地訪問所有業務邏輯,而受限模塊(RestrictedModule)只能訪問文件讀取操作。
在這種情況下,我們可以在門面外再嵌套門面來解決接口權限問題,以供不同的模塊訪問。
// 子系統:文件讀取
class FileReader {
public void read(String filePath) {
System.out.println("讀取文件:" + filePath);
// 具體的讀取邏輯...
}
}
// 子系統:文件寫入
class FileWriter {
public void write(String filePath, String content) {
System.out.println("寫入文件:" + filePath);
// 具體的寫入邏輯...
}
}
// 子系統:文件壓縮
class FileCompressor {
public void compress(String filePath, String destinationPath) {
System.out.println("壓縮文件:" + filePath + " -> " + destinationPath);
// 具體的壓縮邏輯...
}
}
// 通用模塊門面
class GeneralFacade {
private FileReader fileReader;
private FileWriter fileWriter;
private FileCompressor fileCompressor;
public GeneralFacade() {
this.fileReader = new FileReader();
this.fileWriter = new FileWriter();
this.fileCompressor = new FileCompressor();
}
public void processFile(String filePath, String content, String destinationPath) {
fileReader.read(filePath);
fileWriter.write(filePath, content);
fileCompressor.compress(filePath, destinationPath);
}
public void read(String filePath) {
fileReader.read(filePath);
}
}
// 受限模塊門面
class RestrictedFacade {
private GeneralFacade generalFacade = new GeneralFacade();
public void readRestrictedFile(String filePath) {
generalFacade.read(filePath);
}
}
// 客戶端代碼
public class Client {
public static void main(String[] args) {
GeneralFacade generalFacade = new GeneralFacade();
generalFacade.processFile("file.txt", "Hello World!", "compressed.zip");
RestrictedFacade restrictedFacade = new RestrictedFacade();
restrictedFacade.readRestrictedFile("file.txt");
}
}在上述示例中,我們使用了兩個不同的門面:GeneralFacade和RestrictedFacade。GeneralFacade提供了完整的訪問子系統的方法(processFile),而RestrictedFacade僅提供了受限的文件讀取方法(readRestrictedFile)。
通過不同的門面對象,通用模塊可以訪問所有子系統功能,而受限模塊只能訪問特定的子系統功能。
總結
通過使用門面模式,我們可以簡化復雜系統的調用過程,提高代碼的可維護性和可讀性。門面模式將子系統進行封裝,并提供一個簡單的接口給客戶端,隱藏了子系統的復雜性,同時解耦了客戶端與子系統之間的依賴關系。



































