一、享元模式介绍
1. 解决的问题
次要解决载有大量对象时,可能造成内存溢出的问题。
2. 定义
享元模式是一种结构型设计模式,它摒弃了在每个对象中保留所有数据的形式,通过共享多个对象所共有的雷同状态,能在无限的内存容量中载入更多对象。
3. 利用场景
- 在程序必须反对大量对象且没有足够的内存容量时应用享元模式。
二、享元模式优缺点
1. 长处
- 程序中有很多类似对象,那就能节俭大量内存。
2. 毛病
- 可能须要就义执行速度来换取内存,因为每次调用向享元办法时都须要从新计算局部情景数据。
- 代码会变得更加简单。如:为什么要拆分一个实体的状态?
三、享元模式利用实例:高校选课系统优化
1. 实例场景
大家在学校选课时,应该都遇到过高峰期选课零碎无奈应用,只能眼巴巴地等技术人员修复。
为什么选课零碎说崩就崩呢?有没有一些简略的办法就能对这类场景做优化呢?
还真有!选课零碎次要就是要获取咱们的选课信息,在咱们填写选课信息后,提交到服务器,由服务起解决选课信息。
一般来讲,选课零碎应该都是为每个学生抉择的每个课程创立一个对象,而后提交到服务器。
这种设计其实在访问量不大的状况下没有什么问题,但高并发状况下,假如每个人的课程信息对象均匀大小为 1M ,500 个选课申请仅课程信息对象的内存就达到 500M ,节约了大量资源,这种大量内存耗费也容易导致 Out Of Memory。
接下来,就以选课零碎作为场景,介绍一下如何用享元模式优化。
2. 享元模式实现
2.1 工程构造
flyweight-pattern└─ src ├─ main │ └─ java │ └─ org.design.pattern.flyweight │ ├─ model │ │ ├─ Course.java │ │ └─ Student.java │ ├─ factory │ │ └─ CourseFactory.java │ └─ service │ ├─ CourseSelectionService.java │ └─ impl │ └─ CourseSelectionServiceImpl.java └─ test └─ java └─ org.design.pattern.flyweight.test └─ CourseSelectionTest.java
2.2 代码实现
2.2.1 实体
课程
/** * 课程 */@AllArgsConstructor@Getterpublic class Course { /** * 课程id */ private String id; /** * 课程名 */ private String name; /** * 学分 */ private String credit;}
学生
/** * 学生信息 */@Getter@AllArgsConstructorpublic class Student { /** * 学号 */ private String id; /** * 姓名 */ private String name;}
2.2.2 工厂
课程工厂
/** * 课程工厂 */public class CourseFactory { private static final Logger log = LoggerFactory.getLogger(CourseFactory.class); /** * 课程对象池 */ private static Map<String, Course> courseMap = new ConcurrentHashMap<String, Course>(); /** * 获取课程 * * @param dataMap 课程数据 * @return Course */ public static Course getCourse(Map<String, String> dataMap) { String id = dataMap.get("id"); if (ObjectUtils.isEmpty(id)) { log.warn("course id is empty"); return null; } if (ObjectUtils.isNotEmpty(courseMap.get(id))) { log.info("get cache course {}", id); return courseMap.get(id); } else { log.info("create new course {}", id); return createCourse(id, dataMap.get("name"), dataMap.get("credit")); } } /** * 创立课程 * * @param id 课程id * @param name 课程名称 * @param credit 学分 */ public static Course createCourse(String id, String name, String credit) { if (ObjectUtils.isNotEmpty(id) && ObjectUtils.isNotEmpty(name) && ObjectUtils.isNotEmpty(credit)) { Course course = new Course(id, name, credit); courseMap.put(id, course); return course; } return null; }}
2.2.3 服务
选课服务接口
/** * 选课服务接口 */public interface CourseSelectionService { /** * 选课 * * @param student 学生 * @param courseDataMap 课程数据 */ void selectCourse(Student student, Map<String, String> courseDataMap);}
选课服务实现类
/** * 选课服务实现 */public class CourseSelectionServiceImpl implements CourseSelectionService { private static final Logger log = LoggerFactory.getLogger(CourseFactory.class); /** * 选课 * * @param student 学生 * @param courseDataMap 课程数据 */ @Override public void selectCourse(Student student, Map<String, String> courseDataMap) { Course course = CourseFactory.getCourse(courseDataMap); if (ObjectUtils.isEmpty(course)) { log.error("course is not exist"); return; } log.info( "student {} select {} course, the course credit is {}", student.getName(), student.getName(), course.getCredit() ); }}
2.3 测试验证
2.3.1 测试验证类
/** * 选课测试类 */public class CourseSelectionTest { private CourseSelectionService courseSelectionService = new CourseSelectionServiceImpl(); @Test public void testSelectCourse() { Map<String, String> courseDataMap = new HashMap<String, String>() { { put("id", "1"); put("name", "高数一"); put("credit", "5"); } }; Student studentA = new Student("1", "张三"); courseSelectionService.selectCourse(studentA, courseDataMap); Student studentB = new Student("2", "李四"); courseSelectionService.selectCourse(studentB, courseDataMap); }}
2.3.2 测试后果
17:22:37.377 [main] INFO o.d.p.f.factory.CourseFactory - create new course 117:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - student 张三 select 张三 course, the course credit is 517:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - get cache course 117:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - student 李四 select 李四 course, the course credit is 5Process finished with exit code 0
四、享元模式构造
享元模式只是一种优化。在利用该模式之前,要确定程序中存在大量相似对象同时占用内存相干的内存耗费问题,并且确保该问题无奈应用其余更好的形式来解决。
享元(Flyweight)类蕴含原始对象中局部能在多个对象中共享的状态。
同一享元对象可在许多不同情景中应用。享元中存储的状态被称为 “外在状态” 。传递给享元办法的状态被称为 “外在状态” 。
情景 (Context)类蕴含原始对象中各不相同的外在状态。
情景与享元对象组合在一起就能示意原始对象的全副状态。
- 通常状况下,原始对象的行为会保留在享元类中。因而享元办法必须提供局部外在状态作为参数。但也能够将行为挪动到情景类中,而后将连入的享元作为单纯的数据对象。
- 客户端(Client)负责计算或者存储享元的外在状态。在客户端看来,享元是一种可在运行时进行配置的模板对象,具体的配置形式为向其办法中传入一些情景数据参数。
享元工厂(Flyweight Factory)会对已有享元的缓存池进行治理。
有了工厂后,客户端就无需间接创立享元,只需调用工厂并向其传递指标享元的一些外在状态即可。工厂会依据参数在之前已创立的享元中进行查找,找到满足条件的享元将其返回,否则依据参数新建享元。
设计模式并不难学,其自身就是多年教训提炼出的开发指导思想,关键在于多加练习,带着应用设计模式的思维去优化代码,就能构建出更正当的代码。
源码地址:https://github.com/yiyufxst/design-pattern-java
参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深刻设计模式:https://refactoringguru.cn/design-patterns/catalog