当前位置: 首页 > news >正文

Java设计模式之《外观模式》

目录

1、外观模式

1.1、定义

1.2、核心思想:

1.3、角色

1.4、优缺点

2、实现

3、使用场景


前言

关于Java的设计模式分类如下:

关于亨元模式的结构如下:


1、外观模式

1.1、定义

        外观模式是一种结构型设计模式,它为一组复杂的子系统接口提供了一个更简洁、统一的接口。这个模式定义了一个更高层次的接口,使得子系统更容易使用。

1.2、核心思想:

         隐藏系统的复杂性,并向客户端提供一个可以访问系统的、简化的接口。它就像是整个子系统的一个“门面”或“接待员”,客户端不需要了解系统内部的细节,只需要和这个“门面”打交道即可。

类比:

        想象一下餐厅的点餐过程。作为一个顾客(客户端),你不需要直接与后厨的厨师、切菜工、洗碗工(各个子系统)进行复杂的交互。你只需要和服务员(外观)沟通,告诉他你想要什么菜。服务员接收你的简单订单,然后他负责去协调后厨各个部门完成一系列复杂的工作(准备食材、烹饪、装盘等),最后将美味的菜肴送到你面前。服务员就充当了这个“外观”的角色。

1.3、角色

外观模式通常包含以下角色:

1、外观:

知道哪些子系统类负责处理客户端的请求。将客户端的请求代理给相应的子系统对象。

2、附加外观:

(可选)当一个外观变得过于复杂时,可以创建多个附加外观,将职责划分得更清晰。客户端和子系统都可以使用附加外观。

3、子系统:

由多个类或组件组成,实现子系统的功能。

处理由外观对象指派的任务,但对客户端一无所知(即不持有外观的引用)。

4、客户端:

通过调用外观提供的方法来与子系统进行交互,而不是直接调用子系统对象。

1.4、优缺点

1、优点:

简化客户端代码: 

客户端不再需要直接与复杂的子系统交互,代码变得清晰简洁。

降低耦合度: 

        将客户端与子系统解耦,使子系统的变化更容易管理。只要外观接口不变,子系统内部的修改不会影响到客户端。

提供了一个清晰的入口点:

 特别是对于层次化的结构,外观定义了系统的入口点,简化了系统的使用和理解。

2、缺点:

不符合开闭原则:

       当子系统增加新功能或改变时,通常需要修改外观类。不过,可以通过引入抽象外观和具体子外观来缓解这个问题,但这会增加系统的复杂性。

可能成为“上帝对象”: 

如果设计不当,外观类可能会承担过多的职责,变成一个庞大而难以维护的类。


2、实现

        用一个家庭影院系统来举例。开启家庭影院看电影需要一系列复杂的操作:开灯、开投影仪、开音响、设置投影仪输入模式、放下屏幕等。

1、没有外观模式的情况:

代码示例如下:

// 子系统类:电灯
class Light {public void on() { System.out.println("打开电灯"); }public void off() { System.out.println("关闭电灯"); }public void dim(int level) { System.out.println("调暗电灯到 " + level + "%"); }
}// 子系统类:投影仪
class Projector {public void on() { System.out.println("打开投影仪"); }public void off() { System.out.println("关闭投影仪"); }public void setInput(String input) { System.out.println("设置投影仪输入为: " + input); }
}// 子系统类:音响
class Amplifier {public void on() { System.out.println("打开音响"); }public void off() { System.out.println("关闭音响"); }public void setVolume(int level) { System.out.println("设置音量为: " + level); }
}// 子系统类:屏幕
class Screen {public void up() { System.out.println("收起屏幕"); }public void down() { System.out.println("放下屏幕"); }
}// 客户端代码 - 非常复杂!
public class ClientWithoutFacade {public static void main(String[] args) {Light light = new Light();Projector projector = new Projector();Amplifier amp = new Amplifier();Screen screen = new Screen();// 想看电影,需要一步步操作System.out.println("准备看电影...");light.dim(10);      // 调暗灯光screen.down();      // 放下屏幕projector.on();     // 打开投影仪projector.setInput("HDMI");amp.on();           // 打开音响amp.setVolume(5);// ... 还有其他操作System.out.println("开始播放电影...");// 看完电影还要一步步关闭System.out.println("\n电影结束,关闭设备...");// ... 反向操作所有步骤amp.off();projector.off();screen.up();light.on();}
}

2、使用外观模式后:

代码示例:

// 外观类:家庭影院外观
class HomeTheaterFacade {private Light light;private Projector projector;private Amplifier amp;private Screen screen;public HomeTheaterFacade(Light light, Projector projector, Amplifier amp, Screen screen) {this.light = light;this.projector = projector;this.amp = amp;this.screen = screen;}// 提供一个高度简化的“一键观影”方法public void watchMovie() {System.out.println("准备看电影...");light.dim(10);screen.down();projector.on();projector.setInput("HDMI");amp.on();amp.setVolume(5);System.out.println("影院已就绪,开始享受电影吧!");}// 提供一个高度简化的“一键结束”方法public void endMovie() {System.out.println("关闭家庭影院...");amp.off();projector.off();screen.up();light.on();System.out.println("影院已关闭。");}
}// 客户端代码 - 变得极其简单!
public class ClientWithFacade {public static void main(String[] args) {// 初始化子系统组件(通常由依赖注入框架完成)Light light = new Light();Projector projector = new Projector();Amplifier amp = new Amplifier();Screen screen = new Screen();// 创建外观,并组合所需的子系统HomeTheaterFacade homeTheater = new HomeTheaterFacade(light, projector, amp, screen);// 使用外观提供的高级接口homeTheater.watchMovie(); // 一键开启System.out.println("\n正在播放《教父》...\n");homeTheater.endMovie();   // 一键关闭}
}

3、使用场景

1. Spring Framework 中的外观模式

代码示例如下:

// Spring的JdbcTemplate就是一个经典的外观
@Repository
public class UserRepository {@Autowiredprivate JdbcTemplate jdbcTemplate; // 外观public void saveUser(User user) {// 极其简单的接口,隐藏了所有JDBC的复杂性jdbcTemplate.update("INSERT INTO users (name, email) VALUES (?, ?)",user.getName(), user.getEmail());}public List<User> findAll() {// 简化的查询接口return jdbcTemplate.query("SELECT * FROM users",(rs, rowNum) -> new User(rs.getInt("id"),rs.getString("name"),rs.getString("email")));}
}

2. SLF4J 日志框架

代码示例如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyService {// 获取外观接口private static final Logger logger = LoggerFactory.getLogger(MyService.class);public void doSomething() {try {// 业务逻辑logger.info("开始执行操作"); // 简化的日志接口// ...logger.debug("调试信息");} catch (Exception e) {logger.error("操作失败", e); // 统一的错误日志接口}}
}

3. 微服务中的API网关

代码示例如下:

// 模拟API网关外观
public class ApiGateway {private UserService userService;private OrderService orderService;private PaymentService paymentService;private AuthService authService;public ApiGateway() {this.userService = new UserService();this.orderService = new OrderService();this.paymentService = new PaymentService();this.authService = new AuthService();}// 提供统一的API入口public Response handleRequest(Request request) {// 身份验证if (!authService.validateToken(request.getToken())) {return Response.unauthorized();}// 路由到相应的服务switch (request.getPath()) {case "/users":return userService.handle(request);case "/orders":return orderService.handle(request);case "/payments":return paymentService.handle(request);default:return Response.notFound();}}
}

最佳实践和注意事项

  1. 不要过度使用:如果子系统本身很简单,就不需要外观模式,否则会增加不必要的抽象层。

  2. 保持外观简洁:外观应该提供真正简化的接口,而不是成为另一个复杂的层。

  3. 考虑使用接口:可以为外观定义接口,这样更容易替换不同的实现或进行测试。


总结

        外观模式其核心价值在于简化接口,降低复杂度。当你有一个复杂的子系统,并且希望为其提供一个更简单、更清晰的接口时,或者当你想将子系统与客户端解耦时,外观模式是一个绝佳的选择。

        它在 Java 的日志系统、框架和工具库中得到了广泛的应用。


参考文章:

1、设计模式结构型——外观模式-CSDN博客https://blog.csdn.net/tszc95/article/details/131817470?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522bba385534f448f909cb3ef323553d396%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=bba385534f448f909cb3ef323553d396&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~top_click~default-2-131817470-null-null.nonecase&utm_term=%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4450

http://www.xdnf.cn/news/1377919.html

相关文章:

  • 大模型安全概述、LlamaFirewall
  • 深度学习---卷积神经网络CNN
  • Git-远程操作
  • AI-Agent 深度科普:从概念到架构、应用与未来趋势
  • JVM之【Java对象在内存中的结构】
  • Linux--->网络编程(TCP并发服务器构建:[ 多进程、多线程、select ])
  • Linux 系统调优与CPU-IO-网络内核参数调优
  • MySQL InnoDB vs MyISAM
  • 深度学习——卷积神经网络CNN(原理:基本结构流程、卷积层、池化层、全连接层等)
  • LeetCode - 反转链表 / K 个一组翻转链表
  • day2_softmax回归的实现 李沐动手学深度学习pytorch记录
  • 神经网络学习笔记12——高效卷积神经网络架构MobileNet
  • PLC_博图系列☞基本指令”S_ODT:分配接通延时定时器参数并启动“
  • leecode-三数之和
  • 如何防御安全标识符 (SID) 历史记录注入
  • 【Linux实时内核机制】ww_rt_mutex 的contending_lock异常问题
  • wireshark解析FLV插件分享
  • Unity Shader unity文档学习笔记(二十一):几种草体的实现方式(透明度剔除,GPU Instaning, 曲面细分+几何着色器实现)
  • HTML5超详细学习内容
  • GPIO推挽和开漏的名称由来和本质含义
  • FactoryBean接口作用
  • 使用Stone 3D快速制作第一人称视角在线小游戏
  • 【PyTorch】基于YOLO的多目标检测项目(二)
  • 基于Cursor AI IDE的Vue3留言板系统实战:从零搭建到智能优化全流程
  • 《金融对账系统雪崩隐患的深度复盘与架构重生》
  • 从CTFshow-pwn入门-pwn40理解64位栈溢出不都需要堆栈平衡
  • 致远OA新闻公告讨论调查信息查询SQL
  • Linux操作系统——TCP服务端并发模型
  • 域名、ip、DSN、URL
  • 虚拟机逃逸攻防演练