「設計模式你學全了嗎」享元模式:減少對象數量

「設計模式你學全了嗎」享元模式:減少對象數量

前言

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

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

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

系列文章回顧

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

結構型——享元模式 Flyweight Pattern

引子

主要用於減少創建對象的數量,以減少內存佔用和提高性能。

在享元模式中通常會出現工廠模式,需要創建一個享元工廠來負責維護一個享元池(Flyweight Pool)用於存儲具有相同內部狀態的享元對象。

最經典的享元模式代碼:

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

定義

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

兩個概念:

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

在享元類中要將內部狀態和外部狀態分開處理,通常將內部狀態作為享元類的成員變量,而外部狀態通過注入的方式添加到享元類中。

類圖

如果看不懂UML類圖,可以先粗略瀏覽下該圖,想深入瞭解的話,可以繼續谷歌,深入學習:

「設計模式你學全了嗎」享元模式:減少對象數量

享元模式包含如下角色:

  • Flyweight: 抽象享元類
  • ConcreteFlyweight: 具體享元類
  • UnsharedConcreteFlyweight: 非共享具體享元類
  • FlyweightFactory: 享元工廠類
「設計模式你學全了嗎」享元模式:減少對象數量

時序圖

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

我們可以大致瀏覽下時序圖,如果感興趣的小夥伴可以去深究一下:

「設計模式你學全了嗎」享元模式:減少對象數量

代碼實現

代碼參考:

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

假設:我們有一個繪圖的應用程序,通過它我們可以出繪製各種各樣的形狀、顏色的圖形,那麼這裡形狀和顏色就是內部狀態

了,通過享元模式我們就可以實現該屬性的共享了。

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

<code>

public

 

abstract

 

class

 

Shape

 {    

public

 

abstract

 

void

 

draw

(

)

;}/<code>

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

<code>

public

 

class

 

Circle

 

extends

 

Shape

{    

private

 String color;    

public

 

Circle

(String color)

{        

this

.color = color;    }    

public

 

void

 

draw

()

 

{        System.out.println(

"畫了一個"

 + color +

"的圓形"

);    }}/<code>

享元工廠類FlyweightFactory:

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

<code>

public

 

class

 

FlyweightFactory

{    

static

 Map shapes = 

new

 HashMap();    

public

 

static

 Shape 

getShape

(

String key

)

{        Shape shape = shapes.

get

(key);        /<code>

客戶端調用:

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

<code>

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()+

"中顏色的圓形"

);    }}/<code>

使用場景舉例

如果一個系統中存在大量的相同或者相似的對象,由於這類對象的大量使用,會造成系統內存的耗費,可以使用享元模式來減少系統中對象的數量。

Integer 中的享元模式

<code>

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);    }/<code>

輸出是

<code>truefalse/<code>

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

IntegerCache 緩存類:

<code>/

/是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 

/<code>

其他

同理,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/

優缺點

優點

  • 享元模式的優點在於它能夠極大的減少系統中對象的個數
  • 享元模式由於使用了外部狀態,外部狀態相對獨立,不會影響到內部狀態,所以享元模式使得享元對象能夠在不同的環境被共享。

缺點

  • 由於享元模式需要區分外部狀態和內部狀態,使得應用程序在某種程度上來說更加複雜化了。

參考

  • 《HEAD FIRST設計模式》
  • https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/flyweight.html
  • https://www.cnblogs.com/chenssy/p/3330555.html
  • 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

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


分享到:


相關文章: