【软考】设计模式之外观模式

发布于:2024-05-23 ⋅ 阅读:(115) ⋅ 点赞:(0)

1. 说明
  • 1.为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  • 2.Facade Pattern。
  • 3.是一种结构型设计模式,提供了一种简化的接口,用于访问
  • 4.这种模式的主要目标是简化客户端与子系统之间的交互,同时降低系统的耦合度
  • 5.在外观模式中,通常会有一个外观类(Facade),它充当客户端和子系统之间的中介。
  • 6.外观类知道哪些子系统负责处理请求,并将客户端的请求代理给适当的子系统对象。
  • 7.通过这种方式,客户端只需要与外观类进行交互,而不需要了解系统内部的具体细节和复杂性。
2. 应用场景
  • 1.当系统的复杂度较高时,可以使用外观模式来简化系统的使用。通过提供一个简单的接口,可以将系统的复杂性内部化,对外提供一个简单的接口,使得使用者更加容易使用。
  • 2.当系统中存在多个包含关系复杂的接口时,可以使用外观模式进行封装。将多个复杂的接口封装成一个简单的接口,可以减少客户端所需处理的对象数目,并使得子系统使用起来更加容易。
  • 3.当系统需要对外封闭时,可以使用外观模式来进行封装。外界只能通过一个统一的接口来访问系统,这样可以有效提高系统的安全性。
  • 4.当系统需要进行重构时,可以使用外观模式来进行重构。将系统功能进行重组,减少耦合,从而提高系统的灵活性和可扩展性。
3. 结构图

在这里插入图片描述

4. 构成
  • 1.Facade(外观类):知道哪些子系统负责处理请求;将客户的请求代理给适当的子系统对象。这是模式的核心部分,为子系统的一组接口提供一个统一的高层接口,使得子系统更容易使用。客户端只需要与外观类进行交互,而不需要了解子系统内部的具体细节和复杂性。
  • 2.Subsystem classes(子系统):实现子系统的功能;处理有Facade对象指派的任务;没有Facade的任何相关信息,即没有指向Facade的指针。
  • 3.客户端(Client):客户端与外观类进行交互,通过外观类提供的高层接口访问子系统。客户端不需要了解子系统内部的具体实现细节,只需要知道外观类提供的接口和方法即可。
5. 适用性
  • 1.要为一个复杂子系统提供一个简单接口时,子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类,这使得子系统更具有可重用性,也更容易对子系统进行定制,但也给那些不需要定制子系统的用户带来一些使用上的困难。
  • 2.Facade可以提供一个简单的默认视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。
  • 3.客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
  • 4.当需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让它们仅通过Facade进行通信,从而简化了它们之间的依赖关系。
6. 优点
  • 1.简化客户端操作:外观模式为客户端提供了一个简化的、统一的接口,使得客户端无需关心子系统内部的复杂性和实现细节,从而降低了客户端与子系统之间的耦合度。
  • 2.降低系统复杂性:当系统非常复杂时,外观模式可以隐藏子系统内部的复杂性,使得客户端只需要关注与外观类的交互,而无需了解子系统内部的实现细节。
  • 3.提高系统的可维护性:由于外观类将客户端与子系统的交互进行了封装,当子系统内部发生变化时,只需要修改外观类即可,而无需修改所有客户端代码,从而提高了系统的可维护性。
  • 4.灵活的扩展性:当需要添加新的子系统或修改现有子系统时,只需要在外观类中添加或修改相应的代码,而无需修改客户端代码,从而保持了客户端的稳定性。
  • 5.符合“最少知识原则”:外观模式使得客户端只需要与外观类进行交互,而无需了解子系统内部的具体实现细节,这符合了“最少知识原则”,即一个软件实体应当尽可能少地与其他实体发生相互作用。
7. 缺点
  • 1.不符合“开闭原则”:在某些情况下,当需要为子系统添加新的功能时,可能需要修改外观类的代码,从而违反了“开闭原则”(即对扩展开放,对修改封闭)。
  • 2.可能增加系统复杂性:如果外观类设计得过于复杂,可能会增加系统的整体复杂性,使得系统难以理解和维护。
  • 3.不适用于所有场景:外观模式主要适用于子系统相对独立、接口稳定且客户端不需要直接访问子系统内部细节的场景。如果子系统之间存在紧密的联系或者客户端需要直接访问子系统内部细节,那么使用外观模式可能并不合适。
  • 4.可能隐藏了子系统的错误:由于外观类将客户端与子系统的交互进行了封装,当子系统内部出现错误时,这些错误可能会被外观类隐藏起来,使得客户端难以发现和定位问题。
  • 5.可能增加性能开销:在某些情况下,由于外观类需要处理多个子系统的交互,可能会导致性能下降。特别是在处理大量数据或进行复杂计算时,这种性能开销可能会更加明显。
8. java示例
  • 1.子系统接口
package com.learning.facade;

// 点餐子系统接口
public interface MealSubsystem {
    void order();  
    void prepare();  
    void serve();  
}
  • 2.主餐子系统接口
package com.learning.facade;

// 主菜子系统
public class MainFood implements MealSubsystem {
    @Override  
    public void order() {  
        System.out.println("点主餐");
    }  
  
    @Override  
    public void prepare() {  
        System.out.println("准备主餐");
    }  
  
    @Override  
    public void serve() {  
        System.out.println("端上主餐");
    }  
}  
  • 3.甜品子系统接口
package com.learning.facade;

// 甜品子系统
public class Dessert implements MealSubsystem {
    @Override  
    public void order() {  
        System.out.println("点甜品");
    }  
  
    @Override  
    public void prepare() {  
        System.out.println("准备甜品");
    }  
  
    @Override  
    public void serve() {  
        System.out.println("端上甜品");
    }  
}  
  • 4.汤子系统接口
package com.learning.facade;

// 汤品子系统
public class Soup implements MealSubsystem {
    @Override  
    public void order() {  
        System.out.println("点汤");
    }  
  
    @Override  
    public void prepare() {  
        System.out.println("准备汤");
    }  
  
    @Override  
    public void serve() {  
        System.out.println("端上汤");
    }  
}
  • 4.外观类服务员
package com.learning.facade;

/**
 * 外观类 - 服务员
 */
public class Waiter {
    private MealSubsystem mainFood;
    private MealSubsystem soup;  
    private MealSubsystem dessert;  
  
    public Waiter() {  
        this.mainFood = new MainFood();
        this.soup = new Soup();  
        this.dessert = new Dessert();  
    }  
  
    public void orderMeal() {  
        mainFood.order();
        soup.order();  
        dessert.order();  
  
        // 假设准备和送餐是同时进行的  
        prepareAndServe();  
    }  
  
    private void prepareAndServe() {  
        mainFood.prepare();
        soup.prepare();  
        dessert.prepare();  
  
        mainFood.serve();
        soup.serve();  
        dessert.serve();  
    }  
}  
  • 4.客户端
package com.learning.facade;

// 客户端
public class Client {  
    public static void main(String[] args) {  
        Waiter waiter = new Waiter();
        // 顾客告诉服务员点餐,服务员负责处理整个点餐、准备和送餐流程
        waiter.orderMeal();
    }  
}
  • 5.示例截图
    在这里插入图片描述