【設計模式自習室】享元模式:減小對象數量

【設計模式自習室】享元模式:減小對象數量

前言

《設計模式自習室》系列,顧名思義,本系列文章帶你溫習常見的設計模式。主要內容有:html

  • 該模式的介紹,包括:
  • 引子、意圖(大白話解釋)
  • 類圖、時序圖(理論規範)
  • 該模式的代碼示例:熟悉該模式的代碼長什麼樣子
  • 該模式的優缺點:模式不是萬金油,不能夠濫用模式
  • 該模式的應用案例:瞭解它在哪些重要的源碼中被使用

該系列會逐步更新於個人博客和公衆號(博客見文章底部),也但願各位觀衆老爺可以關注個人我的公衆號:後端技術漫談,不會錯過精彩好看的文章。java

系列文章回顧

  • 【設計模式自習室】開篇:爲何咱們要用設計模式?
  • 【設計模式自習室】建造者模式
  • 【設計模式自習室】原型模式
  • 【設計模式自習室】透徹理解單例模式
  • 【設計模式自習室】理解工廠模式的三種形式
  • 【設計模式自習室】適配器模式
  • 【設計模式自習室】幕後英雄:裝飾模式
  • 【設計模式自習室】橋接模式 Bridge Pattern:處理多維度變化
  • 【設計模式自習室】門面模式 Facade Pattern

結構型——享元模式 Flyweight Pattern

引子

主要用於減小建立對象的數量,以減小內存佔用和提升性能。面試

在享元模式中一般會出現工廠模式,須要建立一個享元工廠來負責維護一個享元池(Flyweight Pool)用於存儲具備相同內部狀態的享元對象。算法

最經典的享元模式代碼:後端

class FlyweightFactory {

    //定義一個HashMap用於存儲享元對象,實現享元池
    private HashMap flyweights = newHashMap();

    public Flyweight getFlyweight(String key){

        //若是對象存在,則直接從享元池獲取
        if(flyweights.containsKey(key)){
            return(Flyweight)flyweights.get(key);
        }

        //若是對象不存在,先建立一個新的對象添加到享元池中,而後返回
        else {
            Flyweight fw = newConcreteFlyweight();
            flyweights.put(key,fw);
            return fw;
        }
    }
}

定義

運用共享技術有效地支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種對象結構型模式。設計模式

兩個概念:數組

  • 內部狀態:在享元對象內部不隨外界環境改變而改變的共享部分。
  • 外部狀態:隨着環境的改變而改變,不可以共享的狀態就是外部狀態。

在享元類中要將內部狀態和外部狀態分開處理,一般將內部狀態做爲享元類的成員變量,而外部狀態經過注入的方式添加到享元類中。緩存

類圖

若是看不懂UML類圖,能夠先粗略瀏覽下該圖,想深刻了解的話,能夠繼續谷歌,深刻學習:
【設計模式自習室】享元模式:減小對象數量安全

享元模式包含以下角色:微信

  • Flyweight: 抽象享元類
  • ConcreteFlyweight: 具體享元類
  • UnsharedConcreteFlyweight: 非共享具體享元類
  • FlyweightFactory: 享元工廠類
    【設計模式自習室】享元模式:減小對象數量

    時序圖

時序圖(Sequence Diagram)是顯示對象之間交互的圖,這些對象是按時間順序排列的。時序圖中顯示的是參與交互的對象及其對象之間消息交互的順序。

咱們能夠大體瀏覽下時序圖,若是感興趣的小夥伴能夠去深究一下:
【設計模式自習室】享元模式:減小對象數量

代碼實現

代碼參考:

https://www.cnblogs.com/chenssy/p/3330555.html

假設:咱們有一個繪圖的應用程序,經過它咱們能夠出繪製各類各樣的形狀、顏色的圖形,那麼這裏形狀和顏色就是內部狀態了,經過享元模式咱們就能夠實現該屬性的共享了。

抽象享元類Flyweight:繪製圖像的抽象方法

public abstract class Shape {
    public abstract void draw();
}

具體享元類ConcreteFlyweight:例子中則是一種繪製某種圖像(圓形)的具體實現類,裏面的顏色則是一個能夠共享的內部對象。

public class Circle extends Shape{
    private String color;
    public Circle(String color){
        this.color = color;
    }

    public void draw() {
        System.out.println("畫了一個" + color +"的圓形");
    }

}

享元工廠類FlyweightFactory:

利用了HashMap保存已經建立的顏色

public class FlyweightFactory{
    static Map<String, Shape> shapes = new HashMap<String, Shape>();

    public static Shape getShape(String key){
        Shape shape = shapes.get(key);
        //若是shape==null,表示不存在,則新建,而且保持到共享池中
        if(shape == null){
            shape = new Circle(key);
            shapes.put(key, shape);
        }
        return shape;
    }

    public static int getSum(){
        return shapes.size();
    }
}

客戶端調用:

調用相同顏色時,會直接從HashMap中取那個顏色的對象,而不會重複建立相同顏色的對象。

public class Client {
    public static void main(String[] args) {
        Shape shape1 = FlyweightFactory.getShape("紅色");
        shape1.draw();

        Shape shape2 = FlyweightFactory.getShape("灰色");
        shape2.draw();

        Shape shape3 = FlyweightFactory.getShape("綠色");
        shape3.draw();

        Shape shape4 = FlyweightFactory.getShape("紅色");
        shape4.draw();

        Shape shape5 = FlyweightFactory.getShape("灰色");
        shape5.draw();

        Shape shape6 = FlyweightFactory.getShape("灰色");
        shape6.draw();

        System.out.println("一共繪製了"+FlyweightFactory.getSum()+"中顏色的圓形");
    }
}

使用場景舉例

若是一個系統中存在大量的相同或者類似的對象,因爲這類對象的大量使用,會形成系統內存的耗費,可使用享元模式來減小系統中對象的數量。

Integer 中的享元模式

public static void main(String[] args) {
        Integer i1 = 12 ;
        Integer i2 = 12 ;
        System.out.println(i1 == i2);

        Integer b1 = 128 ;
        Integer b2 = 128 ;
        System.out.println(b1 == b2);
    }

輸出是

true
false

在Java中,Integer是有緩存池的,緩存了-128~127的int對象

IntegerCache 緩存類:

//是Integer內部的私有靜態類,裏面的cache[]就是jdk事先緩存的Integer。
private static class IntegerCache {
    static final int low = -128;//區間的最低值
    static final int high;//區間的最高值,後面默認賦值爲127,也能夠用戶手動設置虛擬機參數
    static final Integer cache[]; //緩存數組

    static {
        // high value may be configured by property
        int h = 127;
        //這裏能夠在運行時設置虛擬機參數來肯定h  :-Djava.lang.Integer.IntegerCache.high=250
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {//用戶設置了
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, 127);//雖然設置了可是仍是不能小於127
            // 也不能超過最大值
            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        //循環將區間的數賦值給cache[]數組
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    }

    private IntegerCache() {}
}

其餘

同理,Long也有緩存池。

String類定義爲final(不可改變的),JVM中字符串通常保存在字符串常量池中,java會確保一個字符串在常量池中只有一個拷貝,這個字符串常量池在JDK6.0之前是位於常量池中,位於永久代,而在JDK7.0中,JVM將其從永久代拿出來放置於堆中。

詳細可參考:

http://laijianfeng.org/2018/09/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F%E5%8F%8A%E5%85%B8%E5%9E%8B%E5%BA%94%E7%94%A8/

優缺點

優勢

  • 享元模式的優勢在於它可以極大的減小系統中對象的個數。
  • 享元模式因爲使用了外部狀態,外部狀態相對獨立,不會影響到內部狀態,因此享元模式使得享元對象可以在不一樣的環境被共享。

缺點

關注我

我是一名後端開發工程師。

主要關注後端開發,數據安全,爬蟲,物聯網,邊緣計算等方向,歡迎交流。

各大平臺均可以找到我

  • 微信公衆號:後端技術漫談
  • Github:@qqxx6661
  • CSDN:@後端技術漫談
  • 知乎:@後端技術漫談
  • 簡書:@後端技術漫談
  • 掘金:@後端技術漫談

原創博客主要內容

  • Java面試知識點複習全手冊
  • 設計模式/數據結構 自習室
  • Leetcode/劍指offer 算法題解析
  • SpringBoot/SpringCloud菜鳥入門實戰系列
  • 爬蟲相關技術文章
  • 後端開發相關技術文章
  • 逸聞趣事/好書分享/我的興趣

我的公衆號:後端技術漫談

【設計模式自習室】享元模式:減小對象數量
公衆號:後端技術漫談.jpg

若是文章對你有幫助,不妨收藏,投幣,轉發,在看起來~

相關文章
相關標籤/搜索