19 设计模式之享元模式(电影院座位预定案例)
一、享元模式的定义
享元模式是一种结构型设计模式,它通过共享对象来支持大量细粒度的对象,减少内存消耗。享元模式的核心思想是:将对象分为共享部分和非共享部分,只有共享部分是被多个对象共享的,而非共享部分则是每个对象独有的。这样,我们可以大大减少内存中的重复对象。
在开发大型应用时,尤其是在对象数量非常庞大的情况下,内存使用和性能优化变得尤为重要。例如,在电影院座位管理、图形绘制、文字显示等应用中,我们可能会遇到大量重复的对象实例。如果每次都创建独立的对象,就会占用大量内存,从而影响系统的性能。享元模式(Flyweight Pattern)提供了一种有效的解决方案,能够减少对象的创建,优化内存使用。
二、享元模式的角色
- 抽象享元(Flyweight):定义享元对象的接口,包含共享的部分。
- 具体享元(ConcreteFlyweight):实现抽象享元接口,具体的享元对象共享相同的数据。
- 非享元(UnsharedConcreteFlyweight):不需要共享的部分,通常是具体享元对象中的一部分。
- 享元工厂(FlyweightFactory):负责创建和管理享元对象,确保共享对象的复用。
三、例子讲解:电影院座位预定
假设我们在开发一个电影院座位预定系统,影院的座位可以通过一个编号来唯一标识。例如,座位编号为“A1”、“A2”、“B1”等。当用户选择某个座位进行预定时,我们需要管理这些座位的状态:是否被预定。
如果我们为每个座位都创建一个独立的对象,那么当座位很多时,会消耗大量的内存。为了优化内存使用,我们可以采用享元模式,将座位的编号和预定状态作为享元对象,避免重复创建相同编号的座位对象。
1. Seat
接口
首先定义一个座位接口,包含两个方法:reserve()
用于预定座位,isReserved()
用于检查座位是否已预定。
public interface Seat {
void reserve(); // 预订座位的方法
boolean isReserved(); // 检查座位是否已预订
}
2. ConcreteSeat
类(具体享元)
ConcreteSeat
是座位的具体实现,它包含座位编号(seatNumber
)和是否预定的状态(reserved
)。这部分数据是共享的,可以被多个对象复用。
public class ConcreteSeat implements Seat {
private String seatNumber; // 座位编号
private boolean reserved; // 座位是否已预定
public ConcreteSeat(String seatNumber) {
this.seatNumber = seatNumber;
this.reserved = false; // 默认座位未预定
}
@Override
public void reserve() {
this.reserved = true;
}
@Override
public boolean isReserved() {
return reserved;
}
public String getSeatNumber() {
return seatNumber;
}
}
3. SeatFactory
类(享元工厂)
SeatFactory
是享元工厂,它负责创建并管理享元对象。它维护了一个seatMap
,用于缓存已经创建的座位。当请求某个座位时,如果该座位已存在,则直接复用;如果不存在,则创建新的座位对象。
public class SeatFactory {
private static Map<String, Seat> seatMap = new HashMap<>();
public static Seat getSeat(String seatNumber) {
// 如果座位已经存在,则复用
if (!seatMap.containsKey(seatNumber)) {
seatMap.put(seatNumber, new ConcreteSeat(seatNumber)); // 创建新座位
}
return seatMap.get(seatNumber); // 返回已有座位
}
}
4. CinemaHall
类(非享元)
CinemaHall
是一个非享元类,它代表电影院。电影院拥有多个座位,座位的创建通过享元工厂来完成。每个电影院座位对象(Seat
)都是享元对象的复用,而非享元部分是每个座位的预定状态(reserved
)和座位管理(addSeats()
、reserveSeat()
)。
public class CinemaHall {
private String hallName; // 影厅名称
private Map<String, Seat> seats = new HashMap<>(); // 储存该影厅的所有座位
public CinemaHall(String hallName) {
this.hallName = hallName;
}
// 使用享元工厂获取或创建座位
public void addSeats(String seatNumber) {
Seat seat = SeatFactory.getSeat(seatNumber);
seats.put(seatNumber, seat);
}
// 预定座位
public void reserveSeat(String seatNumber) {
Seat seat = seats.get(seatNumber);
if (seat != null && !seat.isReserved()) {
seat.reserve();
System.out.println("座位 " + seatNumber + " 已被预定");
} else {
System.out.println("座位 " + seatNumber + " 已被预定或不存在");
}
}
// 显示座位状态
public void showSeats() {
for (String seatNumber : seats.keySet()) {
System.out.println("座位:" + seatNumber + " 被预定:" + seats.get(seatNumber).isReserved());
}
}
}
5. 测试类(TestFlyweight
)
我们可以通过测试类来验证享元模式的有效性。测试代码通过SeatFactory
来创建和管理座位,确保相同编号的座位不会被重复创建,从而减少内存消耗。
public class TestFlyweight {
public static void main(String[] args) {
CinemaHall cinemaHall = new CinemaHall("影厅一");
// 添加座位
cinemaHall.addSeats("A1");
cinemaHall.addSeats("A2");
cinemaHall.addSeats("B1");
cinemaHall.addSeats("B2");
// 预定座位
cinemaHall.reserveSeat("A1");
cinemaHall.reserveSeat("B1");
// 显示座位状态
cinemaHall.showSeats();
}
}
四、享元模式的优势
- 减少内存消耗:享元模式通过共享相同的数据来减少对象的创建,节省内存空间。
- 提高性能:减少不必要的对象创建,提高系统性能,尤其是在对象数量巨大时。
- 适用于大量细粒度对象的场景:在需要管理大量相似对象的情况下,享元模式能显著提升效率。
五、结论
享元模式通过共享对象来优化内存使用,特别适用于系统中存在大量重复对象的场景。通过享元工厂,我们能够实现对象的复用,从而大大节省内存。在实际开发中,享元模式非常适合用于处理类似座位管理、字符渲染等需要大量对象的场景。通过本例,我们深入理解了享元模式的核心思想及实现方式,并学习了如何应用它来解决内存消耗问题。