設計模式(十二)——享元模式(Integer緩衝池原始碼分析)

十四lin發表於2021-02-05

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

    
}

享元模式在 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 常量池、資料庫連線池

 

僅供參考,有錯誤還請指出!

有什麼想法,評論區留言,互相指教指教。

相關文章