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 常量池、資料庫連線池
僅供參考,有錯誤還請指出!
有什麼想法,評論區留言,互相指教指教。