相信大家日常開發過程中,一個優秀的程序猿寫出的代碼一定要節省空間的,比如節省內存,節省磁盤等等。那么如何通過設計模式來節省內存呢?
1、什么是享元模式?
Use sharing to support large numbers of fine-grained objects efficiently.
享元模式(Flyweight Pattern):使用共享對象可有效地支持大量的細粒度的對象。
說人話:復用對象,節省內存。
2、享元模式定義
①、Flyweight——抽象享元角色
是一個產品的抽象類, 同時定義出對象的外部狀態和內部狀態的接口或實現。
一個對象信息可以分為內部狀態和外部狀態。
內部狀態 :對象可共享出來的信息, 存儲在享元對象內部并且不會隨環境改變而改變,可以作為一個對象的動態附加信息, 不必直接儲存在具體某個對象中, 屬于可以共享的部分。
外部狀態 :對象得以依賴的一個標記, 是隨環境改變而改變的、 不可以共享的狀態。
②、ConcreteFlyweight——具體享元角色
具體的一個產品類, 實現抽象角色定義的業務。該角色中需要注意的是內部狀態處理應該與環境無關, 不應該出現一個操作改變了內部狀態, 同時修改了外部狀態, 這是絕對不允許的。
③、unsharedConcreteFlyweight——不可共享的享元角色
不存在外部狀態或者安全要求(如線程安全) 不能夠使用共享技術的對象, 該對象一般不會出現在享元工廠中。
職責非常簡單, 就是構造一個池容器, 同時提供從池中獲得對象的方法。
3、享元模式通用代碼
/**
* 抽象享元角色
*/
public abstract class Flyweight {
// 內部狀態
private String instrinsic;
// 外部狀態 通過 final 修改,防止修改
protected final String extrinsic;
protected Flyweight(String extrinsic) {
this.extrinsic = extrinsic;
}
// 定義業務操作
public abstract void operate();
public String getInstrinsic() {
return instrinsic;
}
public void setInstrinsic(String instrinsic) {
this.instrinsic = instrinsic;
}
}
/**
* 具體享元角色1
*/
public class ConcreteFlyweight1 extends Flyweight{
protected ConcreteFlyweight1(String extrinsic) {
super(extrinsic);
}
@Override
public void operate() {
System.out.println("具體享元角色1");
}
}
/**
* 具體享元角色2
*/
public class ConcreteFlyweight2 extends Flyweight{
protected ConcreteFlyweight2(String extrinsic) {
super(extrinsic);
}
@Override
public void operate() {
System.out.println("具體享元角色2");
}
}
public class FlyweightFactory {
// 定義一個池容器
private static HashMap< String,Flyweight > pool = new HashMap< >();
// 享元工廠
public static Flyweight getFlyweight(String extrinsic){
// 需要返回的對象
Flyweight flyweight = null;
// 池中沒有該對象
if(pool.containsKey(extrinsic)){
flyweight = pool.get(extrinsic);
}else{
// 根據外部狀態創建享元對象
flyweight = new ConcreteFlyweight1(extrinsic);
// 放置到池中
pool.put(extrinsic,flyweight);
}
return flyweight;
}
}
4、通過享元設計文本編輯器
假設文本編輯器只包含文字編輯功能,而且只記錄文字和格式兩部分信息,其中格式包括文字的字體型號、大小、顏色等信息。
4.1 普通實現
通常設計是把每個文字看成一個單獨對象。
package com.itcoke.designpattern.flyweight.edittext;
/**
* 單個文字對象
*/
public class Character {
// 字符
private char c;
// 字體型號
private String font;
// 字體大小
private int size;
// 字體顏色
private int colorRGB;
public Character(char c, String font, int size, int colorRGB){
this.c = c;
this.font = font;
this.size = size;
this.colorRGB = colorRGB;
}
@Override
public String toString() {
return String.valueOf(c);
}
}
/**
* 編輯器實現
*/
public class Editor {
private ArrayList< Character > chars = new ArrayList< >();
public void appendCharacter(char c, String font, int size, int colorRGB){
Character character = new Character(c,font,size,colorRGB);
chars.add(character);
}
public void display(){
System.out.println(chars);
}
}
客戶端:
public class EditorClient {
public static void main(String[] args) {
Editor editor = new Editor();
editor.appendCharacter('A',"宋體",11,0XFFB6C1);
editor.appendCharacter('B',"宋體",11,0XFFB6C1);
editor.appendCharacter('C',"宋體",11,0XFFB6C1);
editor.display();
}
}
4.2 享元模式改寫
上面的問題很容易發現,每一個字符就會創建一個 Character 對象,如果是幾百萬個字符,那內存中就會存在幾百萬的對象,那怎么去節省這些內存呢?
其實,分析一下,對于字體的格式,通常不會有很多,于是我們可以把字體格式設置為享元,也就是上面說的可以共享的內部狀態。
內部狀態(共享):字體類型、大小、顏色
外部狀態(不共享):字符
于是代碼改寫如下:
public class CharacterStyle {
// 字體型號
private String font;
// 字體大小
private int size;
// 字體顏色
private int colorRGB;
public CharacterStyle(String font, int size, int colorRGB) {
this.font = font;
this.size = size;
this.colorRGB = colorRGB;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CharacterStyle that = (CharacterStyle) o;
return size == that.size &&
colorRGB == that.colorRGB &&
Objects.equals(font, that.font);
}
@Override
public int hashCode() {
return Objects.hash(font, size, colorRGB);
}
}
public class CharacterStyleFactory {
private static final Map< CharacterStyle,CharacterStyle > mapStyles = new HashMap< >();
public static CharacterStyle getStyle(String font, int size, int colorRGB){
CharacterStyle newStyle = new CharacterStyle(font,size,colorRGB);
if(mapStyles.containsKey(newStyle)){
return mapStyles.get(newStyle);
}
mapStyles.put(newStyle,newStyle);
return newStyle;
}
}
public class Character {
private char c;
private CharacterStyle style;
public Character(char c, CharacterStyle style) {
this.c = c;
this.style = style;
}
@Override
public String toString() {
return String.valueOf(c);
}
}
public class Editor {
private List< Character > chars = new ArrayList< >();
public void appendCharacter(char c, String font, int size, int colorRGB){
Character character = new Character(c,CharacterStyleFactory.getStyle(font,size,colorRGB));
chars.add(character);
}
public void display(){
System.out.println(chars);
}
}
5、享元模式在 java.lang.Integer 中應用
看下面這段代碼,打印結果是啥?
public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 56;
Integer i2 = 56;
Integer i3 = 129;
Integer i4 = 129;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
為什么是這種結果呢?
首先說一下 Integer i = 59;底層執行了:Integer i = Integer.valueOf(59); 這是自動裝箱。
int j = i; 底層執行了:int j = i.intValue(); 這是自動拆箱。
然后我們Integer.valueOf() 方法:
再看 IntegerCache 源碼:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
其實這就是我們前面說的享元對象的工廠類,緩存 -128 到 127 之間的整型值,這是最常用的一部分整型值,當然JDK 也提供了方法來讓我們可以自定義緩存的最大值。
6、享元模式優點
減少應用程序創建的對象, 降低程序內存的占用, 增強程序的性能。
但它同時也提高了系統復雜性, 需要分離出外部狀態和內部狀態, 而且外部狀態具有固化特性, 不應該隨內部狀態改變而改變, 否則導致系統的邏輯混亂。
7、享元模式應用場景
①、系統中存在大量的相似對象。
②、細粒度的對象都具備較接近的外部狀態, 而且內部狀態與環境無關, 也就是說對象沒有特定身份。
③、需要緩沖池的場景。
-
內存
+關注
關注
8文章
3034瀏覽量
74136 -
代碼
+關注
關注
30文章
4801瀏覽量
68735 -
設計模式
+關注
關注
0文章
53瀏覽量
8645 -
線程
+關注
關注
0文章
505瀏覽量
19705
發布評論請先 登錄
相關推薦
評論