找回密码
 立即注册
首页 业界区 业界 [设计模式/Java] 设计模式之门面模式(外观模式)【20】 ...

[设计模式/Java] 设计模式之门面模式(外观模式)【20】

匣卒 前天 22:03
概述 : 门面模式 := 外观模式 := Facade Pattern

产生背景


  • 软件开发过程中,我们经常会遇到复杂系统,其中包含多个子系统和接口。在这种情况下,为了简化客户端的调用过程提高代码的可维护性和可读性,我们可以使用门面模式。
模式定义


  • 门面模式(Facade Pattern)也叫做外观模式,是一种结构型设计模式
它提供一个统一的接口,封装了一个或多个子系统的复杂功能,并向客户端提供一个简单的调用方式
通过引入门面客户端无需直接与子系统交互,而只需要通过门面来与子系统进行通信。
模式的组成


  • 门面(Facade):门面角色是门面模式的核心,它封装了系统内部复杂子系统的接口,为客户端提供一个简单的高层接口。门面角色知道哪些子系统负责处理请求,并将请求转发给相应的子系统进行处理。
  • 子系统(Subsystem):子系统角色是实际执行系统功能的组件。每个子系统都有自己的职责和行为,通过门面角色对外提供服务。
  • 客户端(Client):客户端角色通过调用门面角色提供的高层接口来使用系统功能,而无需直接与子系统交互。
门面模式中,门面角色充当了客户端子系统之间的中介者隐藏了子系统的复杂性简化了客户端的调用过程
客户端只需要与门面角色进行交互,而不需要了解和处理子系统的具体细节


  • 【特别注意】


  • 门面对象只是提供一个访问子系统的一个路径而已,它不应该也不能参与具体的业务逻辑;
  • 否则,就会产生一个倒依赖的问题:子系统必须依赖门面才能被访问,这是设计上一个严重错误,不仅会违反了单一职责原则,同时也破坏了系统的封装性
适用场景


  • 当一个系统有很多复杂的子系统时,可以使用门面模式将其封装起来,隐藏内部复杂性,简化客户端的调用。
  • 当需要将客户端复杂的子系统解耦,降低系统之间的依赖时,可以使用门面模式
模式特点

优点


  • 简化客户端的调用过程,隐藏了子系统的复杂性,提供了一个统一的接口,客户端无需了解子系统的具体实现。
  • 减少系统的相互依赖,解耦了客户端与子系统之间的依赖关系。
  • 提高了代码的可维护性和可读性
缺点


  • 门面模式可能会导致门面类变得庞大承担过多的责任
  • 如果需要修改子系统的功能,可能需要修改门面类
门面模式的优化

在实际应用中,我们可以对门面模式进行一些优化和扩展。以下是几个常见的优化实现方式:
子系统解耦


  • 门面类可以通过委托调用子系统的功能,而不是直接依赖具体的子系统
这样可以使得子系统能够独立演化,不受门面类的影响。
  1. // 门面类
  2. class Facade {
  3.     private SubSystemInterface subSystemA;
  4.     private SubSystemInterface subSystemB;
  5.     public Facade() {
  6.         subSystemA = new ConcreteSubSystemA();
  7.         subSystemB = new ConcreteSubSystemB();
  8.     }
  9.     // 提供给客户端的接口
  10.     public void operation() {
  11.         subSystemA.operation();
  12.         subSystemB.operation();
  13.     }
  14. }
  15. // 子系统接口
  16. interface SubSystemInterface {
  17.     void operation();
  18. }
  19. // 具体的子系统A
  20. class ConcreteSubSystemA implements SubSystemInterface {
  21.     public void operation() {
  22.         // 实现具体的功能
  23.     }
  24. }
  25. // 具体的子系统B
  26. class ConcreteSubSystemB implements SubSystemInterface {
  27.     public void operation() {
  28.         // 实现具体的功能
  29.     }
  30. }
复制代码
多个门面类


  • 门面已经庞大到不能忍受的程度承担过多的责任时,可以考虑使用多个门面类
  • 每个门面类负责与特定的子系统交互,原则上建议按照功能拆分
比如,一个数据库操作的门面可以拆分为查询门面、删除门面、更新门面等。
  1. // 子系统A的门面类
  2. class SubSystemAFacade {
  3.     private SubSystemA subSystemA;
  4.     public SubSystemAFacade() {
  5.         subSystemA = new SubSystemA();
  6.     }
  7.     // 提供给客户端的接口
  8.     public void operation() {
  9.         subSystemA.operationA();
  10.     }
  11. }
  12. // 子系统B的门面类
  13. class SubSystemBFacade {
  14.     private SubSystemB subSystemB;
  15.     public SubSystemBFacade() {
  16.         subSystemB = new SubSystemB();
  17.     }
  18.     // 提供给客户端的接口
  19.     public void operation() {
  20.         subSystemB.operationB();
  21.     }
  22. }
复制代码
通过上述优化实现方式,我们能够灵活地应对不同的需求和场景,提高了系统的可扩展性和维护性。
门面嵌套


  • 假设我们有一个文件处理系统,其中包括3个子系统:
文件读取(FileReader)、文件写入(FileWriter)和文件压缩(FileCompressor)。


  • 现在有2个模块来访问该子系统:


  • 通用模块(GeneralModule)可以完整地访问所有业务逻辑,而受限模块(RestrictedModule)只能访问文件读取操作。


  • 在这种情况下,我们可以在门面外再嵌套门面来解决接口权限问题,以供不同的模块访问。
  1. // 子系统:文件读取
  2. class FileReader {
  3.     public void read(String filePath) {
  4.         System.out.println("读取文件:" + filePath);
  5.         // 具体的读取逻辑...
  6.     }
  7. }
  8. // 子系统:文件写入
  9. class FileWriter {
  10.     public void write(String filePath, String content) {
  11.         System.out.println("写入文件:" + filePath);
  12.         // 具体的写入逻辑...
  13.     }
  14. }
  15. // 子系统:文件压缩
  16. class FileCompressor {
  17.     public void compress(String filePath, String destinationPath) {
  18.         System.out.println("压缩文件:" + filePath + " -> " + destinationPath);
  19.         // 具体的压缩逻辑...
  20.     }
  21. }
  22. // 通用模块门面
  23. class GeneralFacade {
  24.     private FileReader fileReader;
  25.     private FileWriter fileWriter;
  26.     private FileCompressor fileCompressor;
  27.     public GeneralFacade() {
  28.         this.fileReader = new FileReader();
  29.         this.fileWriter = new FileWriter();
  30.         this.fileCompressor = new FileCompressor();
  31.     }
  32.     public void processFile(String filePath, String content, String destinationPath) {
  33.         fileReader.read(filePath);
  34.         fileWriter.write(filePath, content);
  35.         fileCompressor.compress(filePath, destinationPath);
  36.     }
  37.    
  38.     public void read(String filePath) {
  39.         fileReader.read(filePath);
  40.     }
  41.    
  42. }
  43. // 受限模块门面
  44. class RestrictedFacade {
  45.     private GeneralFacade generalFacade = new GeneralFacade();
  46.    
  47.     public void readRestrictedFile(String filePath) {
  48.         generalFacade.read(filePath);
  49.     }
  50. }
  51. // 客户端代码
  52. public class Client {
  53.     public static void main(String[] args) {
  54.         GeneralFacade generalFacade = new GeneralFacade();
  55.         generalFacade.processFile("file.txt", "Hello World!", "compressed.zip");
  56.         RestrictedFacade restrictedFacade = new RestrictedFacade();
  57.         restrictedFacade.readRestrictedFile("file.txt");
  58.     }
  59. }
复制代码

  • 在上述示例中,我们使用了2个不同的门面:GeneralFacade和RestrictedFacade。


  • GeneralFacade提供了完整的访问子系统的方法(processFile)
  • 而RestrictedFacade仅提供了受限的文件读取方法(readRestrictedFile)
通过不同的门面对象通用模块可以访问所有子系统功能,而受限模块只能访问特定的子系统功能。
案例实践

CASE 门面模式的简单实现

SubSystemA / SubSystemB


  • 子系统A
  1. // 子系统A
  2. public class SubSystemA {
  3.     public void operationA() {
  4.         System.out.println("子系统A的操作");
  5.     }
  6. }
复制代码

  • 子系统B
  1. public class SubSystemB {
  2.     public void operationB() {
  3.         System.out.println("子系统B的操作");
  4.     }
  5. }
复制代码

  • 子系统C
  1. public class SubSystemC {
  2.     public void operationC() {
  3.         System.out.println("子系统C的操作");
  4.     }
  5. }
复制代码
Facade/门面类
  1. public class Facade {
  2.     private SubSystemA subSystemA;
  3.     private SubSystemB subSystemB;
  4.     private SubSystemC subSystemC;
  5.     public Facade() {
  6.         subSystemA = new SubSystemA();
  7.         subSystemB = new SubSystemB();
  8.         subSystemC = new SubSystemC();
  9.     }
  10.     // 提供简单的接口给客户端调用,隐藏了子系统的复杂性
  11.     public void operation() {
  12.         subSystemA.operationA();
  13.         subSystemB.operationB();
  14.         subSystemC.operationC();
  15.     }
  16. }
复制代码
CASE 电商系统


  • 场景描述:
假设我们的电子商务系统包含了订单管理库存管理支付管理等子系统。
为了简化客户端的调用过程,我们可以使用门面模式来封装这些子系统,并提供一个统一的接口。
OrderService/订单管理子系统
  1. // 订单管理子系统
  2. class OrderService {
  3.     public void createOrder() {
  4.         // 创建订单的具体实现
  5.     }
  6. }
复制代码
InventoryService/库存管理子系统
  1. // 库存管理子系统
  2. class InventoryService {
  3.     public void checkStock() {
  4.         // 检查库存的具体实现
  5.     }
  6. }
复制代码
PaymentService/支付管理子系统
  1. // 支付管理子系统
  2. class PaymentService {
  3.     public void makePayment() {
  4.         // 支付的具体实现
  5.     }
  6. }
复制代码
ECommerceFacade/电子商务门面类
  1. // 电子商务门面类
  2. class ECommerceFacade {
  3.     private OrderService orderService;
  4.     private InventoryService inventoryService;
  5.     private PaymentService paymentService;
  6.     public ECommerceFacade() {
  7.         orderService = new OrderService();
  8.         inventoryService = new InventoryService();
  9.         paymentService = new PaymentService();
  10.     }
  11.     // 提供给客户端的接口
  12.     public void placeOrder() {
  13.         orderService.createOrder();
  14.         inventoryService.checkStock();
  15.         paymentService.makePayment();
  16.     }
  17. }
复制代码

  • 我们创建了一个电子商务门面类(ECommerceFacade),它封装了订单管理、库存管理和支付管理等子系统,并提供了一个简单的接口(placeOrder)供客户端调用。
这样,客户端只需要通过门面类来完成下单操作,而无需直接与子系统交互。
CASE Shape(形状接口) 与 ShapeMaker(形状创建器外观类)

场景描述


  • 我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。下一步是定义一个外观类 ShapeMaker。
  • ShapeMaker 类使用实体类来代表用户对这些类的调用。
  • FacadePatternDemo 类使用 ShapeMaker 类来显示结果。
1.png

Shape :抽象接口
  1. public interface Shape {
  2.    void draw();
  3. }
复制代码
Rectangle / Square :具体的接口实现类(子系统)


  • Rectangle
  1. public class Rectangle implements Shape {
  2.    @Override
  3.    public void draw() {
  4.       System.out.println("Rectangle::draw()");
  5.    }
  6. }
复制代码

  • Square
  1. public class Square implements Shape {
  2.    @Override
  3.    public void draw() {
  4.       System.out.println("Square::draw()");
  5.    }
  6. }
复制代码

  • Circle
  1. public class Circle implements Shape {
  2.    @Override
  3.    public void draw() {
  4.       System.out.println("Circle::draw()");
  5.    }
  6. }
复制代码
ShapeMaker(外观类)
  1. public class ShapeMaker {
  2.    private Shape circle;
  3.    private Shape rectangle;
  4.    private Shape square;
  5.    public ShapeMaker() {
  6.       circle = new Circle();
  7.       rectangle = new Rectangle();
  8.       square = new Square();
  9.    }
  10.    public void drawCircle(){
  11.       circle.draw();
  12.    }
  13.    public void drawRectangle(){
  14.       rectangle.draw();
  15.    }
  16.    public void drawSquare(){
  17.       square.draw();
  18.    }
  19. }
复制代码
FacadePatternDemo


  • 使用该外观类画出各种类型的形状。
  1. public class FacadePatternDemo {
  2.    public static void main(String[] args) {
  3.       ShapeMaker shapeMaker = new ShapeMaker();
  4.       shapeMaker.drawCircle();
  5.       shapeMaker.drawRectangle();
  6.       shapeMaker.drawSquare();      
  7.    }
  8. }
复制代码
out
  1. Circle::draw()
  2. Rectangle::draw()
  3. Square::draw()
复制代码
Y 推荐文献


  • 设计模式之总述 - 博客园/千千寰宇
X 参考文献


  • 外观模式 - 菜鸟教程
  • 一文搞懂设计模式—门面模式 - CSDN
    本文作者:        千千寰宇   
    本文链接:         https://www.cnblogs.com/johnnyzen   
    关于博文:评论和私信会在第一时间回复,或直接私信我。   
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA     许可协议。转载请注明出处!
    日常交流:大数据与软件开发-QQ交流群: 774386015        【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!   

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册