Java中的外观模式
综述
本文总结外观模式的定义, 特点, 使用场景并给出了具体的示例.
外观模式的定义
外观模式(门面模式)是一种结构型设计模式. 其主要目的是为复杂系统提供一个简化的接口. 帮助客户端代码与系统的子系统进行交互, 同时还可以省略大量的细节. 这种设计模式可以称得上设计模式中最简单的模式, 甚至可以不加"之一". 因为绝大部分的「封装」操作都可以归类到外观模式当中, 可以说很多新手开发者在没有接触过外观模式的情况下已经在不知不觉间用到了外观模式.
外观模式的角色组成
在外观模式的完整模型中三个角色, 但在一些简化模型中外部调用者被省略掉了
- 外部调用者: 外部调用者通过外观类调用子系统的功能, 在通常的模型中是隐形的, 但完整的模型中需要有它
- 外观类: 外观类持有子系统实例, 对调用者隐藏方法的调用细节, 调用者可以通过调用外观类的方法, 调用子系统提供的功能
- 子系统: 对外提供具体的实现功能, 模式要求至少有一个子系统
外观模式的优缺点
它的优点在于:
- 提供统一接口: 通过外观类, 将系统的复杂性隐藏起来, 提供了一个简单易用的接口给调用者.
- 隐藏底层细节: 迪米特法则又称为最少知识原则, 此原则认为, 一个对象应当对其他对象尽可能少的了解. 而外观模式是迪米特法则的最佳实践方式之一, 它降低了调用者和子系统之间的耦合度, 使得调用者不需要了解系统内部的具体实现细节.
- 解耦调用者和子系统: 外观类提供了一个单一的入口, 有助于提高系统的封装性和安全性.
而缺点则是:
- 不符合开闭原则: 如果要新增或者修改子系统的功能, 需要修改外观类
- 不适合大规模系统: 当子系统过于复杂且分散时, 或者子系统之间的交互频繁改变时, 外观模式可能也会随之频繁修改. 使用外观模式可能导致外观类变得庞大且复杂.
外观模式的使用场景
外观模式的使用场景很简单
- 当子系统提供了过剩的自定义功能时. 注意, 这里过剩的评价标准是外部调用者用不着. 比如子系统提供对某个参数设置 1 - 100 档位的自定义功能, 但是调用者只需要 1 和 100 总计2个档位, 这就可以称为过剩.
- 当调用者用到了某个系统中的多个功能, 但从角色定位来说, 它不是这个系统的一员时. 这时候可以认为这个由多个子系统组成的模块应该有一个统一的接口对外提供服务. 这个统一的接口就是外观类.
所以从以上两点可以看出外观模式和封装的概念非常的相似. 事实上外观模式可以看作封装在设计模式上的拓展实现. 封装是面向对象编程的基础概念, 它可以应用于类/对象等层面, 主要目的是隐藏数据和实现细节, 通过提供公共接口来控制访问. 而外观模式关注的是隔离调用者和子系统.
这里可以用两个生活场景进行比喻说明
旅行社
旅行社通常会将多种服务, 如机票预订、酒店预订、用车服务、导游安排、门票购买等打包成一个统一的旅游套餐, 类似于「西安7天游」「广州1日游」之类的称呼. 这个旅游套餐就像是外观模式的实例, 这里子系统就是这一个个的服务提供者, 而外观类就是旅行社本身. 「西安7天游」「广州1日游」是外观类对外提供的隐藏内部子系统细节的方法, 而购买服务的顾客就是外部调用者.
装修公司
装修公司可能会提供各种家居装修包, 比如简约风格、现代风格或者新中式风格. 每个装修包包括了室内设计、材料选择、施工服务和家具搭配等. 业主可以选择一个装修包来完成整个家居装修, 而不必自行挑选和协调每个装修细节. 这里准备购买装修服务的业主就是外部调用者, 而装修公司就是外观类, 室内设计服务, 材料采购, 联系监工, 施工服务和家具定制/家具采购等都是被隐藏起来的子系统.
当然, 实际应用场景同样非常的多,典型的比如
Junit
JUnit 是一个广泛用于 Java 单元测试的框架. 尽管它的主要功能是帮助开发人员编写和运行单元测试, 但它也是一个很好的外观模式的例子. 通过 JUnit, 开发人员可以简单地编写测试用例和断言, 而无需直接与底层的测试框架或测试运行器交互.
Hibernate
Hibernate 是一个流行的对象关系映射(ORM)框架, 它简化了 Java 对象与数据库表之间的映射关系. 虽然 Hibernate 的主要目的是将对象持久化到数据库中, 但它也隐藏了底层数据库访问和 JDBC 操作的复杂性, 为开发人员提供了一个简单而强大的接口.
外观模式的示例
以之前的旅行社旅游产品为例子
//外观类
public class TravelPackageFacade {
private FlightBookingService flightBookingService;
private HotelBookingService hotelBookingService;
private CarRentalService carRentalService;
private TourGuideService tourGuideService;
private TicketPurchaseService ticketPurchaseService;
public TravelPackageFacade() {
// 初始化各个子系统服务
this.flightBookingService = new FlightBookingService();
this.hotelBookingService = new HotelBookingService();
this.carRentalService = new CarRentalService();
this.tourGuideService = new TourGuideService();
this.ticketPurchaseService = new TicketPurchaseService();
}
// 提供一个简化的接口方法,封装整个旅游套餐购买过程
public void bookTravelPackage(String destination, int duration) {
System.out.println("购买旅行社的 " + destination + duration + "天游 产品");
// 简化的购买流程,调用各个子系统服务
this.flightBookingService.bookFlight(destination, duration);
this.hotelBookingService.bookHotel(destination, duration);
this.carRentalService.rentCar(destination);
this.tourGuideService.arrangeTourGuide(destination);
this.ticketPurchaseService.purchaseTickets(destination);
System.out.println("\n" + destination + duration + "天游 产品预定成功");
}
}
//各个子项目
public class FlightBookingService {
public void bookFlight(String destination, int duration) {
System.out.println("预定前往 " + destination + " 的机票及 " + duration + " 后的返程票");
}
}
public class HotelBookingService {
public void bookHotel(String destination, int duration) {
System.out.println("预定 " + destination + " 的酒店 " + duration + " 天");
}
}
public class CarRentalService {
public void rentCar(String destination) {
System.out.println("出租 " + destination + " 当地的旅游专车服务");
}
}
public class TourGuideService {
public void arrangeTourGuide(String destination) {
System.out.println("安排 " + destination + " 当地的导游");
}
}
public class TicketPurchaseService {
public void purchaseTickets(String destination) {
System.out.println("购买 " + destination + " 当地旅游景区套票");
}
}
//最后是外部调用者
public class Main {
public static void main(String[] args) {
var facade = new TravelPackageFacade();
facade.bookTravelPackage("西安",7);
}
}
总结
外观模式理解简单应用广泛, 作为封装概念在设计模式上的某种实现, 只要是熟悉面向对象的开发者, 理解起来应该不需要超过半个小时, 甚至可能平时工作时不知不觉就用到了. 唯一的难点可能在于什么时候改用, 什么时候可以不用. 而这一点只能由开发者自己在开发中慢慢体会.
标签:外观,Java,调用者,destination,模式,public,子系统 From: https://www.cnblogs.com/dwcg/p/18326745