主要解決載有大量對象時,可能形成內存溢出的問題。java
享元模式是一種結構型設計模式,它摒棄了在每一個對象中保存全部數據的方式,經過共享多個對象所共有的相同狀態,能在有限的內存容量中載入更多對象。git
你們在學校選課時,應該都遇到太高峯期選課系統沒法使用,只能眼巴巴地等技術人員修復。github
爲何選課系統說崩就崩呢?有沒有一些簡單的方法就能對這類場景作優化呢?設計模式
還真有!選課系統主要就是要獲取咱們的選課信息,在咱們填寫選課信息後,提交到服務器,由服務起處理選課信息。緩存
通常來說,選課系統應該都是爲每一個學生選擇的每一個課程建立一個對象,而後提交到服務器。服務器
這種設計其實在訪問量不大的狀況下沒有什麼問題,但高併發狀況下,假設每一個人的課程信息對象平均大小爲 1M ,500 個選課請求僅課程信息對象的內存就達到 500M ,浪費了大量資源,這種大量內存消耗也容易致使 Out Of Memory。併發
接下來,就以選課系統做爲場景,介紹一下如何用享元模式優化。ide
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
課程高併發
/** * 課程 */ @AllArgsConstructor @Getter public class Course { /** * 課程id */ private String id; /** * 課程名 */ private String name; /** * 學分 */ private String credit; }
學生測試
/** * 學生信息 */ @Getter @AllArgsConstructor public class Student { /** * 學號 */ private String id; /** * 姓名 */ private String name; }
課程工廠
/** * 課程工廠 */ 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; } }
選課服務接口
/** * 選課服務接口 */ 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() ); } }
/** * 選課測試類 */ 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); } }
17:22:37.377 [main] INFO o.d.p.f.factory.CourseFactory - create new course 1 17:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - student 張三 select 張三 course, the course credit is 5 17:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - get cache course 1 17:22:37.380 [main] INFO o.d.p.f.factory.CourseFactory - student 李四 select 李四 course, the course credit is 5 Process finished with exit code 0
享元模式只是一種優化。在應用該模式以前,要肯定程序中存在大量相似對象同時佔用內存相關的內存消耗問題,而且確保該問題沒法使用其餘更好的方式來解決。
享元(Flyweight)類包含原始對象中部分能在多個對象中共享的狀態。
同一享元對象可在許多不一樣情景中使用。享元中存儲的狀態被稱爲 「內在狀態」 。傳遞給享元方法的狀態被稱爲 「外在狀態」 。
情景 (Context)類包含原始對象中各不相同的外在狀態。
情景與享元對象組合在一塊兒就能表示原始對象的所有狀態。
享元工廠(Flyweight Factory)會對已有享元的緩存池進行管理。
有了工廠後,客戶端就無需直接建立享元,只需調用工廠並向其傳遞目標享元的一些內在狀態便可。工廠會根據參數在以前已建立的享元中進行查找,找到知足條件的享元將其返回,不然根據參數新建享元。
設計模式並不難學,其自己就是多年經驗提煉出的開發指導思想,關鍵在於多加練習,帶着使用設計模式的思想去優化代碼,就能構建出更合理的代碼。
源碼地址:https://github.com/yiyufxst/design-pattern-java
參考資料:
小博哥重學設計模式:https://github.com/fuzhengwei/itstack-demo-design
深刻設計模式:https://refactoringguru.cn/design-patterns/catalog