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

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();
    }
}

四、享元模式的优势

  1. 减少内存消耗:享元模式通过共享相同的数据来减少对象的创建,节省内存空间。
  2. 提高性能:减少不必要的对象创建,提高系统性能,尤其是在对象数量巨大时。
  3. 适用于大量细粒度对象的场景:在需要管理大量相似对象的情况下,享元模式能显著提升效率。

五、结论

        享元模式通过共享对象来优化内存使用,特别适用于系统中存在大量重复对象的场景。通过享元工厂,我们能够实现对象的复用,从而大大节省内存。在实际开发中,享元模式非常适合用于处理类似座位管理、字符渲染等需要大量对象的场景。通过本例,我们深入理解了享元模式的核心思想及实现方式,并学习了如何应用它来解决内存消耗问题。


http://www.kler.cn/a/428686.html

相关文章:

  • Android系统开发(六):从Linux到Android:模块化开发,GKI内核的硬核科普
  • C++《AVL树》
  • uniapp(小程序、app、微信公众号、H5)预览下载文件(pdf)
  • Windows电脑安装USB Redirector并实现内外网跨网USB共享通信访问
  • 使用Chrome和Selenium实现对Superset等私域网站的截图
  • 开源许可证(Open Source Licenses)
  • spring boot 配置文件加载的加载和使用
  • multiprocessing模块怎么使用?
  • 【MIT-OS6.S081作业1.3】Lab1-utilities primes
  • 基于php+mysql的旅游网站——记忆旅行 旅游分享 攻略分享 设计与实现 源码 配置 文档
  • Unity3D 热更新之HybridCLR方案
  • PT8M2102 触控型 8Bit MCU
  • SQL中的通配符:使用LIKE操作符进行模式匹配
  • 大数据治理:构建数据驱动决策的基石
  • ModelArts Standard的WebSocket在线服务全流程开发
  • [Java]项目入门
  • 梧桐数据库半结构化json数据入库及解析
  • 深度学习中注意力机制介绍及seq2seq案例
  • Matlab自学笔记四十四:使用dateshift函数生成日期时间型序列数据
  • 58 基于 单片机的温湿度、光照、电压、电流检测
  • 解决跨域问题方案
  • 高级java每日一道面试题-2024年12月05日-JVM篇-什么是空闲列表?
  • vue中this指针获取不到函数或数据
  • Vue 鼠标滚轮缩放图片的实现
  • Kubernetes 常用操作大全:全面掌握 K8s 基础与进阶命令
  • 基于 Spring Boot + Vue 的宠物领养系统设计与实现