文章目录
- Pre
- 概述
- 在编程中,外观模式是如何工作的?
- [外观设计模式 UML 类图](#外观设计模式 UML 类图)
- 外观类和子系统的关系
- 优点
- 案例
- 小结
Pre
概述
外观设计模式(Facade Pattern)是一种常见的结构型设计模式,它的主要目的是简化复杂系统的使用。可以把它想象成一个"控制面板"或者"遥控器",通过这个控制面板,用户可以轻松操作一个复杂的系统,而不需要关心系统内部是如何运作的。
举个生活中的例子 , 想象一下,你家有一台多功能的家电,比如一台智能电视,它不仅能看电视,还能上网、播放视频、控制智能家居等等。对于电视的操作,你有遥控器,可以通过一些按钮控制各种功能。
- 复杂系统:电视内部的各种硬件(显示屏、网络模块、声音系统、处理器等)和软件(操作系统、应用程序等)。
- 外观模式:遥控器,它将所有复杂的操作集中在几个按钮上,用户只需要按下遥控器上的某个按钮(比如"开机"或"返回主屏幕"),就能触发一系列复杂的操作,而不需要了解电视内部的具体工作原理。
在编程中,外观模式是如何工作的?
外观模式通过为复杂系统提供一个简洁的接口,将复杂的子系统操作封装在一个统一的外观类(Facade)中。使用者只需要与外观类交互,而不需要关心具体的实现细节。
例如,一个复杂系统涉及多个子模块,如果没有外观模式,用户(或程序)在调用时,可能需要依次与每个子模块进行交互,调用很多方法,导致代码非常复杂。通过外观模式,我们可以将这些操作封装成一个简单的方法调用,用户只需要调用外观类的方法即可。
外观模式的好处
- 简化接口:将复杂的系统封装起来,提供一个简单易懂的接口,减少了调用者的复杂度。
- 降低耦合度:外部系统不需要直接依赖复杂的子系统,只需要依赖外观类,减少了系统间的依赖。
- 提高可维护性:如果系统的内部实现发生变化,外观类的接口不变,调用者不需要修改任何代码。
- 方便扩展:新的子系统可以轻松集成,只需要修改外观类,不影响其他部分。
外观设计模式的核心思想就是 简化接口,隐藏复杂性。它提供了一个高层次的接口,简化了系统的使用,减少了客户端与复杂子系统之间的耦合度。这种模式非常适用于需要简化复杂系统、减少外部依赖的场景。
外观设计模式 UML 类图
外观设计模式的 UML 图 主要展示了外观类(Facade)如何通过统一的接口与多个子系统进行交互,并为客户端提供简化的接口
-
Client(客户端):客户端通过调用外观类(Facade)的方法来与子系统进行交互,客户端只需要关注外观类提供的简单接口,而不需要直接操作复杂的子系统。
-
Facade(外观类) :外观类提供了一个统一的接口(如
operation()
),将复杂的操作委托给不同的子系统。客户端通过调用外观类的方法来简化与多个子系统的交互。 -
Subsystem1, Subsystem2, Subsystem3(子系统类) :这些类代表系统中的各个子模块,每个子系统类都有自己复杂的逻辑方法(如
method1()
、method2()
、method3()
),但是客户端不直接调用它们,而是通过外观类来与之交互。
外观类和子系统的关系
- 外观类通过 组合 (
subsystem1
,subsystem2
,subsystem3
)的方式持有多个子系统的实例。 - 外观类的方法(例如
operation()
)在内部调用不同子系统的方法,客户端只需要调用外观类提供的简单接口,而不需要直接与多个子系统交互。
优点
- 简化接口:客户端通过外观类,避免了直接与多个复杂的子系统打交道。
- 降低耦合度:客户端与多个子系统之间的耦合减少,客户端只与外观类交互,而不需要关心子系统的具体实现。
- 增强可维护性:如果子系统的实现发生了变化,客户端无需改动,只需要修改外观类的实现即可。
案例
在构建大型系统时,业务逻辑通常分散在不同的层次之间,涉及多个功能模块,这些模块之间相互依赖,彼此交织,难以快速理解与维护。例如,一个典型的 在线旅游预订系统,其预订流程可能涉及到如下多个子系统或功能模块:
- 航班查询:根据用户输入的起点、终点和日期查询可用航班。
- 酒店预定:查询目标地点的酒店并为用户提供预定选项。
- 旅游套餐推荐:根据用户偏好推荐相关旅游套餐。
- 支付接口对接:与第三方支付接口对接完成支付。
这些环节需要在多个服务、多个方法之间传递数据与控制流。假设需求有所变动,新增了一个业务流程或修改了现有流程,往往需要在不同的层次之间修改代码,甚至可能会影响到系统的其他部分。
外观模式在复杂业务中的应用
外观模式(Facade Pattern) 是一种结构型设计模式,它提供了一个统一的接口,来访问子系统中的一组接口。外观模式通过为复杂子系统提供一个简单的接口,隐藏了系统的复杂性,使得外部调用者只需要关注简洁明了的接口,而不必关心其内部复杂的实现细节。简言之,外观模式就是在复杂的业务子系统上加一个"控制面板"(外观类),通过简单的按钮(外观类的方法)控制复杂的"机器"运转。
在一个典型的 在线旅游预订系统 中,预订业务涉及到多个子系统和服务。通过外观模式,我们可以将所有涉及的业务逻辑统一封装在一个外观类中,而上层业务只需要与该外观类交互即可,无需关心其内部的实现细节。
实战运用
以下代码简化了复杂的业务逻辑,重点体会
1. 项目搭建与基础配置
首先,创建一个 Spring Boot 项目,加入必要的依赖:
xml
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database (for simplicity) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
然后,我们创建相应的实体类,例如 Flight
、Hotel
和 Package
,并为它们创建相应的 Repository 接口:
java
@Entity
public class Flight {
@Id
private Long id;
private String flightNumber;
private String departure;
private String arrival;
// Getters and Setters
}
@Repository
public interface FlightRepository extends JpaRepository<Flight, Long> {
}
2. 构建子系统组件
在旅游系统中,涉及到多个子系统服务,例如航班查询、酒店预定、旅游套餐推荐等。我们为这些子系统服务创建相应的组件类:
航班服务
java
@Service
public class FlightService {
public List<Flight> findAvailableFlights(String departure, String arrival, LocalDate date) {
// 实际查询数据库或调用外部航班API,此处简化逻辑
System.out.println("查询航班:" + departure + " 到 " + arrival + ",日期:" + date);
return List.of(new Flight(1L, "AA123", departure, arrival));
}
}
酒店服务
java
@Service
public class HotelService {
public List<Hotel> findAvailableHotels(String location, LocalDate checkInDate, LocalDate checkOutDate) {
// 查询酒店信息
System.out.println("查询酒店:" + location + ",入住:" + checkInDate + ",退房:" + checkOutDate);
return List.of(new Hotel(1L, "Hotel California", location));
}
}
旅游套餐服务
java
@Service
public class PackageService {
public List<TourPackage> recommendPackages(String destination) {
// 推荐套餐
System.out.println("推荐旅游套餐:" + destination);
return List.of(new TourPackage(1L, "Paris Special", destination));
}
}
3. 创建外观类
接下来,我们创建一个 BookingFacade
类,将多个子系统的功能集中封装在一个外观类中:
java
@Service
public class BookingFacade {
private final FlightService flightService;
private final HotelService hotelService;
private final PackageService packageService;
public BookingFacade(FlightService flightService, HotelService hotelService, PackageService packageService) {
this.flightService = flightService;
this.hotelService = hotelService;
this.packageService = packageService;
}
public boolean bookTravel(String departure, String arrival, LocalDate flightDate,
String hotelLocation, LocalDate checkIn, LocalDate checkOut,
String destination) {
// 查询航班
List<Flight> flights = flightService.findAvailableFlights(departure, arrival, flightDate);
if (flights.isEmpty()) {
return false;
}
// 查询酒店
List<Hotel> hotels = hotelService.findAvailableHotels(hotelLocation, checkIn, checkOut);
if (hotels.isEmpty()) {
return false;
}
// 推荐旅游套餐
List<TourPackage> packages = packageService.recommendPackages(destination);
if (packages.isEmpty()) {
return false;
}
return true;
}
}
4. 在 Controller 中使用外观类
在 Spring Boot 的 Controller 层中,我们可以直接使用 BookingFacade
来简化业务逻辑的调用:
java
@RestController
@RequestMapping("/bookings")
public class BookingController {
private final BookingFacade bookingFacade;
public BookingController(BookingFacade bookingFacade) {
this.bookingFacade = bookingFacade;
}
@PostMapping
public ResponseEntity<String> bookTravel(@RequestBody TravelRequest travelRequest) {
boolean success = bookingFacade.bookTravel(travelRequest.getDeparture(),
travelRequest.getArrival(),
travelRequest.getFlightDate(),
travelRequest.getHotelLocation(),
travelRequest.getCheckIn(),
travelRequest.getCheckOut(),
travelRequest.getDestination());
if (success) {
return ResponseEntity.ok("旅游预定成功");
}
return ResponseEntity.badRequest().body("旅游预定失败");
}
}
通过以上设计,BookingController
只需要关注如何处理用户请求,而具体的业务逻辑(如航班查询、酒店预定、套餐推荐)都已经被封装在 BookingFacade
类中。无论如何修改或扩展预定业务,我们只需要在外观类中进行修改,其他地方的代码不需要做任何变动。
小结
外观模式为系统提供了统一且简洁的接口,同时隐藏了底层复杂的业务逻辑。而 Spring Boot 强大的依赖注入(DI)特性,使得各个服务的整合变得更加灵活与易于管理。
通过这种设计方式,我们能够:
- 降低代码耦合度:将复杂的子系统封装起来,暴露简单的接口,降低了不同模块之间的依赖关系。
- 提升代码可维护性:对于新增需求或业务变更,我们只需要在外观类中进行修改,而不用在多个地方重复修改。
- 增强开发效率:业务逻辑的模块化和统一化,使得开发人员能更加专注于业务实现,而不是处理复杂的交互关系。