1 展示網站項目需求
小型的外包項目,給客戶 A 做一個產品展示網站,客戶 A 的朋友感覺效果不錯,也希望做這樣的產品展示網站,但是要求都有些不同:
1) 有客戶要求以新聞的形式發布
2) 有客戶人要求以博客的形式發布
3) 有客戶希望以微信公眾號的形式發布
2 傳統方案解決網站展現項目
1) 直接復制粘貼一份,然后根據客戶不同要求,進行定制修改
2) 給每個網站租用一個空間
3) 方案設計示意圖
3 傳統方案解決網站展現項目-問題分析
1) 需要的網站結構相似度很高,而且都不是高訪問量網站,如果分成多個虛擬空間來處理,相當於一個相同網站的實例對象很多,造成服務器的資源浪費
2) 解決思路:整合到一個網站中,共享其相關的代碼和數據,對於硬盤、內存、CPU、數據庫空間等服務器資源都可以達成共享,減少服務器資源
3) 對於代碼來說,由於是一份實例,維護和擴展都更加容易
4) 上面的解決思路就可以使用 享元模式 來解決
4 享元模式基本介紹
基本介紹
1) 享元模式(Flyweight Pattern) 也叫 蠅量模式: 運用共享技術有效地支持大量細粒度的對象
2) 常用於系統底層開發,解決系統的性能問題。像數據庫連接池,里面都是創建好的連接對象,在這些連接對象中有我們需要的則直接拿來用,避免重新創建,如果沒有我們需要的,則創建一個
3) 享元模式能夠解決重復對象的內存浪費的問題,當系統中有大量相似對象,需要緩沖池時。不需總是創建新對象,可以從緩沖池里拿。這樣可以降低系統內存,同時提高效率
4) 享元模式經典的應用場景就是池技術了,String 常量池、數據庫連接池、緩沖池等等都是享元模式的應用,享元模式是池技術的重要實現方式
5 享元模式的原理類圖
對類圖的說明
對原理圖的說明-即(模式的角色及職責)
1) FlyWeight 是抽象的享元角色, 他是產品的抽象類, 同時定義出對象的外部狀態和內部狀態(后面介紹) 的接口或實現
2) ConcreteFlyWeight 是具體的享元角色,是具體的產品類,實現抽象角色定義相關業務
3) UnSharedConcreteFlyWeight 是不可共享的角色,一般不會出現在享元工廠。
4) FlyWeightFactory 享元工廠類,用於構建一個池容器(集合), 同時提供從池中獲取對象方法
6 內部狀態和外部狀態
比如圍棋、五子棋、跳棋,它們都有大量的棋子對象,圍棋和五子棋只有黑白兩色,跳棋顏色多一點,所以棋子顏色就是棋子的內部狀態;而各個棋子之間的差別就是位置的不同,當我們落子后,落子顏色是定的,但位置是變化的,所以棋子坐標就是棋子的外部狀態
1) 享元模式提出了兩個要求:細粒度和共享對象。這里就涉及到內部狀態和外部狀態了,即將對象的信息分為兩個部分:內部狀態和外部狀態
2) 內部狀態指對象共享出來的信息,存儲在享元對象內部且不會隨環境的改變而改變
3) 外部狀態指對象得以依賴的一個標記,是隨環境改變而改變的、不可共享的狀態。
4) 舉個例子:圍棋理論上有 361 個空位可以放棋子,每盤棋都有可能有兩三百個棋子對象產生,因為內存空間有限,一台服務器很難支持更多的玩家玩圍棋游戲,如果用享元模式來處理棋子,那么棋子對象就可以減少到只有兩個實例,這樣就很好的解決了對象的開銷問題
7 享元模式解決網站展現項目
1) 應用實例要求
使用享元模式完成,前面提出的網站外包問題
2) 思路分析和圖解(類圖)
WebSite 類
package com.lin.flyweight;
public abstract class WebSite { public abstract void use(User user); }
//共享的部分,內部狀態
class ConcreteWebSite extends WebSite{ private String type = ""; public ConcreteWebSite(String type) { super(); this.type = type; } @Override public void use(User user) { System.out.println("網站的發布形式為:" + type + " " + user.getName() + " 正在使用中...."); } }
WebSiteFactory 類
package com.lin.flyweight;
import java.util.HashMap; public class WebSiteFactory { // 集合充當池的作用 private HashMap<String, ConcreteWebSite> pool = new HashMap<String, ConcreteWebSite>(); // 根據網站的類型返回一個網站,如果沒有就創建一個網站,並放入到池中,並返回 public WebSite getWebSiteCategory(String type) { if(!pool.containsKey(type)) { pool.put(type, new ConcreteWebSite(type)); } return (WebSite)pool.get(type); } // 獲取網站分類的總數 public int getWebSiteCount() { return pool.size(); } }
Client 類
package com.lin.flyweight;
public class Client { public static void main(String[] args) { WebSiteFactory webSiteFactory = new WebSiteFactory(); User user1 = new User("Tom"); User user2 = new User("Jack"); // 新聞類網站 WebSite webSiteCategory = webSiteFactory.getWebSiteCategory("新聞"); webSiteCategory.use(user1); // 博客類網站 WebSite webSiteCategory1 = webSiteFactory.getWebSiteCategory("博客"); webSiteCategory1.use(user1); // 商城類網站 WebSite webSiteCategory2 = webSiteFactory.getWebSiteCategory("商城"); webSiteCategory2.use(user1); // 博客類網站 WebSite webSiteCategory3 = webSiteFactory.getWebSiteCategory("博客"); webSiteCategory3.use(user2); int webSiteCount = webSiteFactory.getWebSiteCount(); System.out.println(webSiteCount); } }
User 類
package com.lin.flyweight; // 外部狀態 public class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
8 享元模式在 JDK-Interger 的應用源碼分析
1) Integer 中的享元模式
2)代碼分析
代碼說明:
package com.lin.flyweight; public class FlyWeight { public static void main(String[] args) { // TODO Auto-generated method stub //如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在 //范圍類,則仍然 new //小結: //1. 在 valueOf 方法中,先判斷值是否在 IntegerCache 中,如果不在,就創建新的 Integer(new), 否則,就直接從 緩存池返回 //2. valueOf 方法,就使用到享元模式 //3. 如果使用 valueOf 方法得到一個 Integer 實例,范圍在 -128 - 127 ,執行速度比 new 快 Integer x = Integer.valueOf(127); // 得到 x 實例,類型
Integer Integer y = new Integer(127); // 得 到 y 實 例 , 類 型
Integer Integer z = Integer.valueOf(127);//.. Integer w = new Integer(127); System.out.println(x.equals(y)); // 大小,true
System.out.println(x == y ); // false
System.out.println(x == z ); // true
System.out.println(w == x ); // false
System.out.println(w == y ); // false Integer x1 = Integer.valueOf(200); Integer x2 = Integer.valueOf(200); System.out.println("x1==x2" + (x1 == x2)); // false } }
9 享元模式的注意事項和細節
1) 在享元模式這樣理解,“享”就表示共享,“元”表示對象
2) 系統中有大量對象,這些對象消耗大量內存,並且對象的狀態大部分可以外部化時,我們就可以考慮選用享元模式
3) 用唯一標識碼判斷,如果在內存中有,則返回這個唯一標識碼所標識的對象,用 HashMap/HashTable 存儲
4) 享元模式大大減少了對象的創建,降低了程序內存的占用,提高效率
5) 享元模式提高了系統的復雜度。需要分離出內部狀態和外部狀態,而外部狀態具有固化特性,不應該隨着內部狀態的改變而改變,這是我們使用享元模式需要注意的地方.
6) 使用享元模式時,注意划分內部狀態和外部狀態,並且需要有一個工廠類加以控制。
7) 享元模式經典的應用場景是需要緩沖池的場景,比如 String 常量池、數據庫連接池
僅供參考,有錯誤還請指出!
有什么想法,評論區留言,互相指教指教。