设计模式之门面模式(外观模式)的原理、组成

笔记哥 / 04-22 / 6点赞 / 0评论 / 576阅读
# 门面模式(外观模式) ## 模式定义 - **门面模式**(`Facade Pattern`)也叫做**外观模式**,是一种**结构型设计模式**。 > 它提供一个**统一的接口**,封装了一个或多个子系统的复杂功能,并向客户端提供一个**简单的调用方式**。 > > 通过引入**门面**,**客户端**无需直接与**子系统**交互,而只需要通过**门面**来与**子系统**进行通信。 ## 模式的组成 - **门面**(`Facade`):门面角色是门面模式的核心,它封装了系统内部复杂子系统的接口,为客户端提供一个简单的高层接口。门面角色知道哪些子系统负责处理请求,并将请求转发给相应的子系统进行处理。 - **子系统**(`Subsystem`):子系统角色是实际执行系统功能的组件。每个子系统都有自己的职责和行为,通过门面角色对外提供服务。 - **客户端**(`Client`):客户端角色通过调用门面角色提供的高层接口来使用系统功能,而无需直接与子系统交互。 > > > 在**门面模式**中,**门面角色**充当了**客户端**和**子系统**之间的**中介者**,**隐藏了子系统的复杂性**,**简化了客户端的调用过程**。 > > **客户端**只需要与**门面角色**进行交互,而不需要了解和处理**子系统的具体细节**。 > - 【特别注意】 > > - **门面对象**只是提供一个**访问子系统的一个路径**而已,它不应该也不能参与具体的业务逻辑; > - 否则,就会产生一个**倒依赖**的问题:**子系统**必须**依赖门面**才能被访问,这是设计上一个**严重错误**,不仅会违反了**单一职责原则**,同时也破坏了**系统的封装性**。 > > ## 适用场景 - 当一个系统有很多复杂的子系统时,可以使用**门面模式**将其封装起来,**隐藏内部复杂性**,简化客户端的调用。 - 当需要将**客户端**与**复杂的子系统**做**解耦**,降低系统之间的依赖时,可以使用**门面模式**。 ## 模式特点 ### 优点 - 简化客户端的调用过程,**隐藏了子系统的复杂性**,提供了一个统一的接口,客户端无需了解子系统的具体实现。 - 减少系统的相互依赖,**解耦**了客户端与子系统之间的依赖关系。 - 提高了代码的**可维护性和可读性**。 ### 缺点 - 门面模式可能会导致**门面类变得庞大**,**承担过多的责任**。 - 如果需要**修改子系统的功能**,可能需要**修改门面类**。 ## 门面模式的优化 > > > 在实际应用中,我们可以对门面模式进行一些优化和扩展。以下是几个常见的优化实现方式: > ### 子系统解耦 - **门面类**可以通过**委托**来**调用子系统**的功能,而不是**直接依赖**于**具体的子系统**。 > > > 这样可以使得**子系统**能够**独立演化**,不受**门面类**的影响。 > ```csharp // 门面类 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() { // 实现具体的功能 } } ``` ### 多个门面类 - 当**门面**已经**庞大到不能忍受的程度**,**承担过多的责任**时,可以考虑使用**多个门面类**。 - **每个门面类**负责与**特定的子系统**交互,原则上建议**按照功能拆分** > > > > > > > > > 比如,一个数据库操作的门面可以拆分为查询门面、删除门面、更新门面等。 > > > > ```csharp // 子系统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(); } } ``` > > > 通过上述优化实现方式,我们能够灵活地应对不同的需求和场景,提高了系统的可扩展性和维护性。 > ### 门面嵌套 - 假设我们有一个**文件处理系统**,其中包括3个子系统: > > > 文件读取(FileReader)、文件写入(FileWriter)和文件压缩(FileCompressor)。 > - 现在有2个模块来访问该子系统: > > - **通用模块**(GeneralModule)可以完整地访问所有业务逻辑,而**受限模块**(RestrictedModule)只能访问文件读取操作。 > > - 在这种情况下,我们可以在**门面**外再**嵌套门面**来解决**接口权限问题**,以供不同的模块访问。 ```csharp // 子系统:文件读取 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"); } } ``` - 在上述示例中,我们使用了2个**不同的门面**:`GeneralFacade`和`RestrictedFacade`。 > > - `GeneralFacade`提供了**完整的访问子系统的方法**(`processFile`) > - 而`RestrictedFacade`仅提供了**受限的文件读取方法**(`readRestrictedFile`) > > > > > 通过**不同的门面对象**,**通用模块**可以访问所有子系统功能,而**受限模块**只能访问特定的子系统功能。 > # 案例实践 ## CASE 门面模式的简单实现 ### SubSystemA / SubSystemB - 子系统A ```csharp // 子系统A public class SubSystemA { public void operationA() { System.out.println("子系统A的操作"); } } ``` - 子系统B ```csharp public class SubSystemB { public void operationB() { System.out.println("子系统B的操作"); } } ``` - 子系统C ```csharp public class SubSystemC { public void operationC() { System.out.println("子系统C的操作"); } } ``` ### Facade/门面类 ```csharp 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(); } } ``` ## CASE 电商系统 - 场景描述: > > > 假设我们的**电子商务系统**包含了**订单管理**、**库存管理**和**支付管理**等子系统。 > > 为了**简化客户端的调用过程**,我们可以使用**门面模式**来封装这些子系统,并提供一个统一的接口。 > ### OrderService/订单管理子系统 ```csharp // 订单管理子系统 class OrderService { public void createOrder() { // 创建订单的具体实现 } } ``` ### InventoryService/库存管理子系统 ```csharp // 库存管理子系统 class InventoryService { public void checkStock() { // 检查库存的具体实现 } } ``` ### PaymentService/支付管理子系统 ```csharp // 支付管理子系统 class PaymentService { public void makePayment() { // 支付的具体实现 } } ``` ### ECommerceFacade/电子商务门面类 ```csharp // 电子商务门面类 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`)供客户端调用。 > > > 这样,客户端只需要通过门面类来完成下单操作,而无需直接与子系统交互。 > ## CASE Shape(形状接口) 与 ShapeMaker(形状创建器外观类) ### 场景描述 - 我们将创建一个 `Shape` 接口和实现了 `Shape` 接口的实体类。下一步是定义一个外观类 `ShapeMaker`。 - `ShapeMaker` 类使用实体类来代表用户对这些类的调用。 - `FacadePatternDemo` 类使用 `ShapeMaker` 类来显示结果。 ![](https://cdn.res.knowhub.vip/c/2504/22/f9b80c53.png?G1cAAER17rxg7Yagfice0wSBBJoBizSCSgnr9e491y3y%2fa7QFJ9e2%2fD14Te1DReyWGYRhR5ICEGVSAaanYEAsl3MiLM7) ### Shape :抽象接口 ```csharp public interface Shape { void draw(); } ``` ### Rectangle / Square :具体的接口实现类(子系统) - Rectangle ```csharp public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } } ``` - Square ```csharp public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } } ``` - Circle ```csharp public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } ``` ### ShapeMaker(外观类) ```csharp public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } } ``` ### FacadePatternDemo - 使用该外观类画出各种类型的形状。 ```csharp public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } } ``` > > > out > ```csharp Circle::draw() Rectangle::draw() Square::draw() ```