设计模式:享元模式(Flyweight)
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/design_pattern/flyweight/
?運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對象。又名“蠅量模式”。
?在Java語言中,String類型就是使用了享元模式。String對象是final類型,對象一旦創(chuàng)建就不可改變。在JAVA中字符串常量都是存在常量池中的,Java會確保一個(gè)字符串常量在常量池中只有一個(gè)拷貝。譬如:
?輸出結(jié)果:true。這就說明了a和b量引用都指向了常量池中的同一個(gè)字符串常量“abc"。這樣的設(shè)計(jì)避免了在創(chuàng)建N多相同對象時(shí)所產(chǎn)生的不必要的大量的資源消耗。
?享元模式采用一個(gè)共享來避免大量擁有相同內(nèi)容對象的開銷。這種開銷最常見、最直觀的就是內(nèi)存的消耗。享元對象能做到共享的關(guān)鍵是區(qū)分內(nèi)蘊(yùn)狀態(tài)(Internal State)和外蘊(yùn)狀態(tài)(External State)。
?一個(gè)內(nèi)蘊(yùn)狀態(tài)是存儲在享元對象內(nèi)部的,并且是不會隨環(huán)境的改變而有所不同。因此,一個(gè)享元可以具有內(nèi)蘊(yùn)狀態(tài)并且可以共享。
?一個(gè)外蘊(yùn)狀態(tài)是隨環(huán)境的改變而改變的、不可以共享的。享元對象的外蘊(yùn)狀態(tài)必須由客戶端保存,并在享元對象被創(chuàng)建之后,在需要使用的時(shí)候再傳入到享元對象內(nèi)部。外蘊(yùn)狀態(tài)不可以影響享元對象的內(nèi)蘊(yùn)狀態(tài),他們是相互獨(dú)立的。
?享元模式可以分成單純享元模式和復(fù)合享元模式。
##單純享元模式
包含的角色:
舉個(gè)簡單例子
1 抽象享元角色
2 具體享元角色
public class ConcreteFlyweight implements Flyweight {private String str;public ConcreteFlyweight(String str){this.str = str;}@Overridepublic void operation(String state){System.out.println("內(nèi)蘊(yùn)狀態(tài):"+str);System.out.println("外蘊(yùn)狀態(tài):"+state);} }3 享元工廠角色
public class FlyWeightFactory {private Map<String,ConcreteFlyweight> flyWeights = new HashMap<String, ConcreteFlyweight>();public ConcreteFlyweight factory(String str){ConcreteFlyweight flyweight = flyWeights.get(str);if(null == flyweight){flyweight = new ConcreteFlyweight(str);flyWeights.put(str, flyweight);}return flyweight;}public int getFlyWeightSize(){return flyWeights.size();} }4 測試代碼
FlyWeightFactory factory = new FlyWeightFactory();Flyweight f1 = factory.factory("a");Flyweight f2 = factory.factory("b");Flyweight f3 = factory.factory("a");f1.operation("a fly weight");f2.operation("b fly weight");f3.operation("c fly weight");System.out.println(f1 == f3);System.out.println(factory.getFlyWeightSize());輸出結(jié)果:
內(nèi)蘊(yùn)狀態(tài):a 外蘊(yùn)狀態(tài):a fly weight 內(nèi)蘊(yùn)狀態(tài):b 外蘊(yùn)狀態(tài):b fly weight 內(nèi)蘊(yùn)狀態(tài):a 外蘊(yùn)狀態(tài):c fly weight true 2##復(fù)合享元模式
?在單純享元模式中,所有的享元對象都是單純享元對象,也就是說都是可以直接共享的,將一些單純享元使用合成模式加以復(fù)合,形成復(fù)合享元對象。這樣的復(fù)合享元對象本身不能共享,但是它們可以分解成單純享元對象,而后者則可以共享。
?包含的角色
變更上面的例子:
1 抽象享元角色(同上)
2 具體享元角色(同上)
3 復(fù)合享元角色
?復(fù)合享元對象是由單純享元對象通過復(fù)合而成的,因此提供了add()這樣的聚集管理方法。由于一個(gè)復(fù)合享元對象具有不同的聚集元素,這些聚集元素在復(fù)合享元對象被創(chuàng)建之后加入,這本身就意味著復(fù)合享元對象的狀態(tài)是會改變的,因此復(fù)合享元對象是不能共享的。
4 享元工廠角色提供兩種不同的方法,一種用于提供單純享元對象,另一種用于提供復(fù)合享元對象。
public class FlyweightCompositeFactory {private Map<String,Flyweight> flyWeights = new HashMap<String, Flyweight>();public Flyweight factory(List<String> compositeStates){ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight();for(String s: compositeStates){compositeFly.add(s, this.factory(s));}return compositeFly;}public Flyweight factory(String s){Flyweight fly = flyWeights.get(s);if(fly == null){fly = new ConcreteFlyweight(s);flyWeights.put(s, fly);}return fly;} }5 測試代碼
List<String> list = new ArrayList<String>();list.add("a");list.add("b");list.add("c");list.add("a");list.add("b");FlyweightCompositeFactory factory = new FlyweightCompositeFactory();Flyweight f1 = factory.factory(list);Flyweight f2 = factory.factory(list);f1.operation("Composite Call");System.out.println("=======");System.out.println("復(fù)合享元模式是否可以共享對象:"+(f1 == f2));String str = "a";Flyweight f3 = factory.factory(str);Flyweight f4 = factory.factory(str);System.out.println("單純享元模式是否可以共享對象:"+(f3 == f4));運(yùn)行結(jié)果:
內(nèi)蘊(yùn)狀態(tài):b 外蘊(yùn)狀態(tài):Composite Call 內(nèi)蘊(yùn)狀態(tài):c 外蘊(yùn)狀態(tài):Composite Call 內(nèi)蘊(yùn)狀態(tài):a 外蘊(yùn)狀態(tài):Composite Call ======= 復(fù)合享元模式是否可以共享對象:false 單純享元模式是否可以共享對象:true?由上例可知:一個(gè)符合享元對象的所有單純享元對象元素的外蘊(yùn)狀態(tài)都是與復(fù)合享元對象的外蘊(yùn)狀態(tài)相等的。一個(gè)復(fù)合享元對象所含有的單純享元對象的內(nèi)蘊(yùn)狀態(tài)一般是不相等的。復(fù)合享元對象是不能共享的。單純享元對象是可以共享的。
?舉個(gè)更形象點(diǎn)的例子,比如去飯店吃飯,菜單只有一份,而每個(gè)顧客點(diǎn)菜品卻各不相同,但是肯定會有重復(fù),我們用上述的享元模式嘗試下模擬代碼情形:
FlyweightCompositeFactory factory = new FlyweightCompositeFactory();List<String> menuList = Arrays.asList("魚香肉絲","宮保雞丁","杭椒牛柳","平鍋魚","番茄炒蛋");Flyweight f1 = factory.factory(menuList.subList(0, 2));Flyweight f2 = factory.factory(menuList.subList(2, 3));f1.operation("customer1的菜單");System.out.println("================");f2.operation("customer2的菜單");代碼輸出:
內(nèi)蘊(yùn)狀態(tài):魚香肉絲 外蘊(yùn)狀態(tài):customer1的菜單 內(nèi)蘊(yùn)狀態(tài):宮保雞丁 外蘊(yùn)狀態(tài):customer1的菜單 ================ 內(nèi)蘊(yùn)狀態(tài):杭椒牛柳 外蘊(yùn)狀態(tài):customer2的菜單優(yōu)缺點(diǎn)
優(yōu)點(diǎn):大幅度降低內(nèi)存中對象的數(shù)量
缺點(diǎn):享元模式使得系統(tǒng)更加復(fù)雜。為了使對象可以共享,需要將一些狀態(tài)外部化,這使得程序的邏輯復(fù)雜化。享元模式將享元對象的狀態(tài)外部化,而讀取外部狀態(tài)使得運(yùn)行時(shí)間稍微變長。
Jdk中的享元模式
java.lang.Integer#valueOf(int)
java.lang.Boolean#valueOf(boolean)
java.lang.Byte#valueOf(byte)
java.lang.Character#valueOf(boolean)
參考資料
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/design_pattern/flyweight/
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
總結(jié)
以上是生活随笔為你收集整理的设计模式:享元模式(Flyweight)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设计模式:组合模式(Composite)
- 下一篇: 设计模式:模板方法模式(Template