设计模式-外观模式(Facade)

1. 概念

  • 外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一群接口。外观模式的主要目的是隐藏系统的复杂性,通过定义一个高层级的接口,使得子系统更容易被使用。在外观模式中,客户端不与每个子系统进行复杂的交互,而是只与提供接口的外观类进行交互

2. 原理结构图

在这里插入图片描述

  • 外观角色(Facade):这是外观模式的核心角色,它提供了一个简化的接口,用于访问子系统中的功能。外观类的作用是封装复杂的子系统操作,让外部客户端无需了解内部细节就能进行交互。
  • 子系统角色(Subsystem):这些是实际执行具体任务的类或模块。它们可能包含多个类和更复杂的逻辑,对于客户端来说,直接与这些子系统交互可能会非常复杂。
  • 客户角色(Client):客户端使用外观类提供的接口与子系统进行交互。通过这种方式,客户端可以简化其代码,因为它只需要与外观类打交道,而不是直接与复杂的子系统打交道。

3. 代码示例

3.1 示例1
  • 有一个复杂的音频播放系统,其中包含多个组件,如CD播放器、MP3播放器、音量控制器等。每个组件都有自己的接口和实现。我们希望提供一个简化的接口,让用户能够轻松地播放音乐,而无需关心底层组件的细节。
// CD播放器接口  
interface CDPlayer {  void play();  void stop();  
}  // CD播放器实现  
class RealCDPlayer implements CDPlayer {  @Override  public void play() {  System.out.println("Playing CD...");  }  @Override  public void stop() {  System.out.println("Stopping CD...");  }  
}  // MP3播放器接口  
interface MP3Player {  void play();  void stop();  
}  // MP3播放器实现  
class RealMP3Player implements MP3Player {  @Override  public void play() {  System.out.println("Playing MP3...");  }  @Override  public void stop() {  System.out.println("Stopping MP3...");  }  
}  // 音量控制器接口  
interface VolumeControl {  void increaseVolume();  void decreaseVolume();  
}  // 音量控制器实现  
class RealVolumeControl implements VolumeControl {  @Override  public void increaseVolume() {  System.out.println("Increasing volume...");  }  @Override  public void decreaseVolume() {  System.out.println("Decreasing volume...");  }  
}class AudioPlayerFacade {  private CDPlayer cdPlayer;  private MP3Player mp3Player;  private VolumeControl volumeControl;  public AudioPlayerFacade() {  cdPlayer = new RealCDPlayer();  mp3Player = new RealMP3Player();  volumeControl = new RealVolumeControl();  }  public void playCD() {  cdPlayer.play();  }  public void playMP3() {  mp3Player.play();  }  public void stop() {  cdPlayer.stop();  mp3Player.stop();  }  public void increaseVolume() {  volumeControl.increaseVolume();  }  public void decreaseVolume() {  volumeControl.decreaseVolume();  }  
}
public class AudioPlayerClient {  public static void main(String[] args) {  AudioPlayerFacade audioPlayerFacade = new AudioPlayerFacade();  audioPlayerFacade.playCD();  audioPlayerFacade.increaseVolume();  audioPlayerFacade.playMP3();  audioPlayerFacade.decreaseVolume();  audioPlayerFacade.stop();  }  
}
  • 输出
Playing CD...
Increasing volume...
Playing MP3...
Decreasing volume...
Stopping CD...
Stopping MP3...
  • 在这个示例中,AudioPlayerFacade 类充当外观类,它封装了 CDPlayer、MP3Player 和 VolumeControl 组件的实例,并提供了简化的接口供客户端使用。客户端代码只需与外观类进行交互,而无需关心底层组件的实现细节。这使得代码更加简洁和易于维护。

3.2 示例2
  • 智能家居系统是一个很好的例子,用于说明外观模式如何简化复杂系统的使用。在这个场景中,用户可能想要控制多个设备,如灯、电视、空调等,每个设备都有自己的控制接口和实现。通过使用外观模式,我们可以提供一个统一的接口,使得用户能够更容易地控制这些设备。
// 灯的接口  
interface Light {  void turnOn();  void turnOff();  
}  // 真实的灯实现  
class RealLight implements Light {  @Override  public void turnOn() {  System.out.println("Light is turned on.");  }  @Override  public void turnOff() {  System.out.println("Light is turned off.");  }  
}  // 电视的接口  
interface TV {  void turnOn();  void turnOff();  void changeChannel(int channel);  
}  // 真实的电视实现  
class RealTV implements TV {  @Override  public void turnOn() {  System.out.println("TV is turned on.");  }  @Override  public void turnOff() {  System.out.println("TV is turned off.");  }  @Override  public void changeChannel(int channel) {  System.out.println("TV channel changed to: " + channel);  }  
}  // 空调的接口  
interface AirConditioner {  void turnOn();  void turnOff();  void setTemperature(int temperature);  
}  // 真实的空调实现  
class RealAirConditioner implements AirConditioner {  @Override  public void turnOn() {  System.out.println("Air conditioner is turned on.");  }  @Override  public void turnOff() {  System.out.println("Air conditioner is turned off.");  }  @Override  public void setTemperature(int temperature) {  System.out.println("Air conditioner set to: " + temperature + " degrees.");  }  
}class SmartHomeFacade {  private Light light;  private TV tv;  private AirConditioner airConditioner;  public SmartHomeFacade() {  light = new RealLight();  tv = new RealTV();  airConditioner = new RealAirConditioner();  }  public void turnOnLight() {  light.turnOn();  }  public void turnOffLight() {  light.turnOff();  }  public void turnOnTV() {  tv.turnOn();  }  public void turnOffTV() {  tv.turnOff();  }  public void changeTVChannel(int channel) {  tv.changeChannel(channel);  }  public void turnOnAirConditioner() {  airConditioner.turnOn();  }  public void turnOffAirConditioner() {  airConditioner.turnOff();  }  public void setAirConditionerTemperature(int temperature) {  airConditioner.setTemperature(temperature);  }  
}public class SmartHomeClient {  public static void main(String[] args) {  SmartHomeFacade smartHomeFacade = new SmartHomeFacade();  // 控制灯  smartHomeFacade.turnOnLight();  // 做一些其他事情...  smartHomeFacade.turnOffLight();  // 控制电视  smartHomeFacade.turnOnTV();  smartHomeFacade.changeTVChannel(5);  // 做一些其他事情...  smartHomeFacade.turnOffTV();  // 控制空调  smartHomeFacade.turnOnAirConditioner();  smartHomeFacade.setAirConditionerTemperature(24);  // 做一些其他事情...  smartHomeFacade.turnOffAirConditioner();  }  
}
  • 将看到如下输出:
Light is turned on.
Light is turned off.
TV is turned on.
TV channel changed to: 5
TV is turned off.
Air conditioner is turned on.
Air conditioner set to: 24 degrees.
Air conditioner is turned off.
  • 在这个例子中,SmartHomeFacade 类充当外观类,它封装了 Light、TV 和 AirConditioner 设备的实例,并提供了统一的接口供客户端使用。客户端代码只需与外观类进行交互,而无需关心底层设备的实现细节。这简化了智能家居系统的使用,并使得

4. 优缺点

  • 主要作用
    • 为复杂的子系统提供一个统一、简化的接口,以便更容易地访问子系统中的功能。
  • 优点
    • 简化接口:外观模式为子系统提供了一个简单、统一的接口,使得客户端能够更方便地访问和操作子系统的功能。这有助于减少客户端需要与之交互的接口数量,降低了系统的复杂性。
    • 解耦子系统与客户端:外观模式隐藏了子系统的内部结构和复杂性,客户端只需与外观类进行交互,无需了解子系统的具体实现。这种解耦使得子系统的变化对客户端的影响最小化,提高了系统的可维护性和可扩展性。
    • 提高系统的安全性:通过限制客户端对子系统的直接访问,外观模式有助于防止客户端对子系统进行不当操作或破坏。外观类可以对外部请求进行验证和过滤,确保只有合法的请求能够到达子系统。
    • 灵活性增强:当需要添加新的子系统功能或修改现有功能时,由于客户端只与外观类交互,因此只需修改外观类而无需修改所有客户端代码。这增强了系统的灵活性和可扩展性。
    • 易于管理和控制:外观类可以统一管理和控制对子系统的访问,这有助于维护系统的一致性和完整性。同时,外观类还可以对子系统的操作进行日志记录、性能监控等,方便后续的管理和维护工作。
  • 缺点
    • 违背开闭原则:当需要添加新的子系统或者修改现有子系统时,可能需要更改外观类的实现,这违反了面向对象设计中的开闭原则。
    • 隐藏细节:外观模式隐藏了子系统细节,可能使客户端难以深入了解或定制子系统行为。
    • 增加系统的复杂性:虽然外观模式简化了客户端与系统的交互,但在系统中引入了一个额外的层次,这可能会增加系统的复杂性。
    • 潜在的性能问题:由于所有请求都通过外观类进行转发,这可能会成为性能瓶颈,尤其是在高并发的场景下。

5. 应用场景

5.1 主要包括以下几个方面
  1. 系统复杂度较高:当系统的某一子系统变得过于复杂,不容易使用时,可以使用外观模式来进行简化。外观模式可以将系统的复杂性内部化,对外提供一个简单的接口,使得使用者更加容易使用。
  2. 存在多个复杂接口:当系统中存在多个接口之间的依赖关系比较复杂时,外观模式可以将这些接口进行封装,将复杂性进行内部化,从而简化其使用和维护。
  3. 需要对外封闭:当系统需要对外封闭,外界只能通过一个统一的接口来访问系统时,外观模式非常适用。它可以提供一个统一的接口,用于访问子系统中的一群接口,从而有效提高系统的安全性。
  4. 系统重构:当系统需要进行重构,需要对原有的代码进行优化和改进时,外观模式也能发挥重要作用。它可以将系统功能进行重组,减少耦合,使得代码更加易于理解和维护,从而提高系统的灵活性和可扩展性。
  5. 多层系统构建:在构建多层系统时,外观模式可以作为每层的入口,简化层间调用。例如,在经典的三层架构中,可以在数据访问层和业务逻辑层、业务逻辑层和表示层之间建立外观,降低耦合度。
  6. 维护遗留系统:对于难以维护和扩展的遗留大型系统,如果它包含重要的功能且新的需求开发必须依赖它,使用外观模式可以提供一个简洁的接口,方便新需求的开发。

5.2 实际应用
  1. 操作系统界面:操作系统提供一个统一的界面(外观类),用户通过这个界面可以方便地使用各种功能,而不需要关心底层子系统的具体实现。这个界面简化了用户的操作,隐藏了子系统的复杂性。
  2. 智能家居系统:智能家居系统可以设计一个统一的控制界面(外观类),用户通过这个界面可以控制所有智能设备,而不需要单独操作每个设备的控制接口。这个界面简化了用户的操作,提高了使用的便捷性。
  3. 游戏开发:游戏引擎可以提供一个简单的接口(外观类),游戏开发者通过这个接口可以方便地使用游戏引擎的各种功能,而不需要直接与底层的子系统打交道。这样,游戏开发者可以更加专注于游戏的逻辑和玩法设计,而不用关心底层的实现细节。
  4. 企业级应用:企业级应用可以设计一个统一的业务操作界面(外观类),用户通过这个界面可以完成跨模块的业务操作,而不需要了解每个模块的具体实现和子系统之间的复杂关系。这样,用户的操作变得更加简单和高效。

6. JDK中的使用

  • Java的集合框架:Java集合框架提供了丰富的集合类,如ArrayList、HashSet等,以及各种迭代器接口。为了简化对这些集合类的操作,Java提供了诸如Collections这样的工具类,它作为集合框架的Facade,提供了静态方法对集合进行排序、搜索、线程安全化等操作。这样,客户端代码就不需要直接与各种集合类交互,而是通过Collections这个统一的接口进行操作,降低了系统的复杂性。
  • Java I/O流:Java的I/O流体系非常复杂,涉及字节流、字符流、输入流、输出流等多种类型。为了方便用户使用,Java提供了诸如Files这样的工具类作为Facade,它提供了静态方法用于文件的读写、复制、移动等操作。这样,用户就不需要深入了解I/O流的各种细节,只需要调用Files类提供的方法即可。
  • Java的JDBC API:JDBC API用于Java程序与数据库进行交互。为了简化数据库操作,Java提供了DataSource作为Facade,它封装了数据库连接池的管理和数据库连接的创建过程。用户只需要通过DataSource获取数据库连接,然后执行SQL语句即可,无需关心连接池的具体实现和细节。

7. 注意事项

  • 明确接口简化目标:外观模式的主要目的是简化复杂系统的接口,因此在设计Facade类时,需要明确哪些接口需要被简化,确保Facade类提供的接口既简洁又能够满足客户端的需求。
  • 避免过度封装:虽然外观模式通过封装子系统的复杂性来简化接口,但过度封装可能导致Facade类过于庞大和复杂,反而增加了系统的维护难度。因此,在封装时要适度,避免将过多子系统的细节暴露给Facade类。
  • 保持子系统的独立性:使用外观模式时,应确保子系统之间的独立性。Facade类不应该直接依赖于子系统的具体实现,而应该通过接口或抽象类与子系统交互。这样可以降低Facade类与子系统之间的耦合度,使得子系统更易于维护和扩展。
  • 考虑系统的可扩展性:在设计Facade类时,应预留一定的扩展空间。随着系统的发展,可能需要添加新的功能或修改现有功能。如果Facade类的设计过于僵化,将难以适应这些变化。因此,在设计时应考虑到未来可能的扩展需求。
  • 注意性能问题:虽然外观模式可以简化接口和降低耦合度,但也可能引入性能问题。如果Facade类中的方法涉及大量计算或资源消耗,那么频繁调用这些方法可能导致性能下降。因此,在使用外观模式时,需要关注性能问题,确保Facade类的实现是高效的。

8. 外观模式 VS 代理模式

模式目的模式架构主要角色应用场景
外观模式简化接口,减少系统的复杂性外观角色和子系统角色复杂的系统且包含多个子系统,简化系统的接口
代理模式控制对被代理类的访问代理角色和目标角色访问对象前进行一些预处理操作的场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/615499.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在前后端一体的项目中引入element-ui,即引入index.js、index.css等文件。

24年接手了一个18年的项目&#xff0c;想使用el-ui的组件库&#xff0c;得自己手动引入。 通过官网可以知道&#xff0c;首先得准备以下文件 <!-- 引入样式 --> <link rel"stylesheet" href"https://unpkg.com/element-ui/lib/theme-chalk/index.css…

C语言:约瑟夫环问题详解

前言 哈喽&#xff0c;宝子们&#xff01;本期为大家带来一道C语言循环链表的经典算法题&#xff08;约瑟夫环&#xff09;。 目录 1.什么是约瑟夫环2.解决方案思路3.创建链表头结点4.创建循环链表5.删除链表6.完整代码实现 1.什么是约瑟夫环 据说著名历史学家Josephus有过以下…

二叉树练习day.9

669.修剪二叉搜索树 链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变…

(弟)递归•斐波那契数、n的k次方

这里是目录哦 题目一&#xff1a;递归计算斐波那契数斐波那契数的定义代码运行截图递归过程递归停止条件&#xff08;1个参数&#xff09;✨非递归实现方法 题目二&#xff1a;递归实现n的k次方代码运行截图递归过程递归停止条件&#xff08;不止1个参数&#xff09;✨ 加油&am…

蓝桥杯之注意事项

1.特殊求解的地方 2.一些数学公式 比如二叉树求全深度数值那道题 3.掌握有关库函数 #include<algorithm> 包含sort&#xff08;&#xff09;函数【排列函数】C sort()排序详解-CSDN博客&#xff0c;next_permutation()函数【求解全排列问题】求解数组大小sizeof(arr…

nginx代理动静态资源

部署nginx参考&#xff1a; 部署nginx 静态资源代理 静态资源验证

Project Euler_Problem 172_Few Repeated Digits_动态规划

原题目&#xff1a; 题目大意&#xff1a;18位数里头&#xff0c;有多少个数&#xff0c;对于每个数字0-9&#xff0c;在这18位里面出现均不超过3次 111222333444555666 布星~~ 112233445566778899 可以~~ 解题思路&#xff1a; 动态规划 代码: ll F[19][3000000];void …

【算法刷题 | 回溯思想 02】4.12(电话号码的字母组合)

文章目录 4.电话号码的字母组合4.1问题4.2解法&#xff1a;回溯4.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;终止条件&#xff08;3&#xff09;遍历过程 4.2.2代码实现 4.电话号码的字母组合 4.1问题 给定一个仅包含数字 2-9 的字符…

别等Sora了!字节跳动旗下国产AI工具Dreamina,AI视频生成虽不完美,但够惊艳!

别等 Sora 了&#xff0c;试试字节跳动的 Dreamina&#xff01;Dreamina 是剪映旗下的一个 AI 创作平台&#xff0c;目前支持「文生图」、「智能画布」和「视频生成」功能。 Dreamina 官网&#xff1a;https://dreamina.jianying.com/ai-tool/home 之前对 Dreamina 的「文生图…

【ENSP】华为三层交换机配置AAA认证,开启telnet服务

配置步骤 1.给交换机配置ip地址&#xff0c;以便登陆 2.配置AAA&#xff0c;用户名&#xff0c;密码&#xff0c;服务类型&#xff0c;用户权限 3.配置接入设备的数量 4.开启telnet服务 LSW2交换机配置 u t m #关闭提示 sys …

25、链表-环形链表

思路&#xff1a; 这道题就是判断链表中是否有环&#xff0c;首先使用集合肯定可以快速地解决&#xff0c;比如通过一个set集合遍历&#xff0c;如果遍历过程中有节点在set中已经存在那么说明存在环。 第二种方式就是通过快慢指针方式寻找环。具体思路就是一个慢指针每次直走一…

MySQL知识整理

MySQL知识整理 基础第一讲&#xff1a;基础架构&#xff1a;一条SQL查询语句是如何执行的&#xff1f;架构尽量减少长连接的原因和方案为什么尽量不要依赖查询缓存 索引第四讲&#xff1a;深入浅出索引&#xff08;上&#xff09;第五讲&#xff1a;深入浅出索引&#xff08;下…