知名做漫畫(huà)網(wǎng)站百度官網(wǎng)進(jìn)入
目錄
- 10、享元模式
- 10.1 享元模式
- 10.2 舉例
- 10.2.1 馬賽克
- 10.2.2 游戲地圖(以草原地圖作為范例)
- 10.3 總結(jié)
10、享元模式
10.1 享元模式
- “享元”則是共享元件的意思
- 享元模式的英文flyweight是輕量級(jí)的意思,這就意味著享元模式能使程序變得更加輕量化
- 當(dāng)系統(tǒng)存在大量的對(duì)象,并且這些對(duì)象又具有相同的內(nèi)部狀態(tài)時(shí),我們就可以用享元模式共享相同的元件對(duì)象,以避免對(duì)象泛濫造成資源浪費(fèi)。
- 測(cè)試類結(jié)構(gòu)
10.2 舉例
10.2.1 馬賽克
- 雖然馬賽克小塊數(shù)量比較多,但經(jīng)過(guò)觀察我們會(huì)發(fā)現(xiàn),
- 分析組成,進(jìn)行歸類后,找到元件只有4種:黑色塊、灰色塊、灰白色塊以及白色塊。
- 我們可以說(shuō),這就是4個(gè)“元”色塊
10.2.2 游戲地圖(以草原地圖作為范例)
- 對(duì)組成部分進(jìn)行歸類分析,找到原件
- 游戲地圖都是由一個(gè)個(gè)小的單元圖塊組成的
- 其中除房屋比較大之外,其他圖塊的尺寸都一樣,它們分別為河流、草地、道路,這些圖塊便是4個(gè)元圖塊
- 分析建模
- 定義一個(gè)圖塊類來(lái)描述圖塊,具體屬性應(yīng)該包括“圖片”和“位置”信息,并且具備按照這些信息去繪制圖塊的能力:Segment
package flyweight;/*** @Description 圖塊類*/ public class Segment {/*** 材質(zhì)圖*/private String image;/*** 位置坐標(biāo)*/private int x,y;/*** 顯式帶參構(gòu)造方法:初始化各參數(shù)* @param image 材質(zhì)圖* @param x 橫坐標(biāo)* @param y 縱坐標(biāo)*/public Segment(String image, int x, int y) {this.image = image;System.out.println("從磁盤(pán)加載[" + image + "]圖片……");this.x = x;this.y = y;}/*** 圖塊繪制方法:按坐標(biāo)位置繪制在地圖上*/public void draw() {System.out.println("在位置[" + x + ":" + y + "]上繪制圖片:[" + image + "]");} }
- 在地圖第一行隨便繪制一些圖塊,Client.test1()
- 在這一步會(huì)發(fā)現(xiàn),圖片加載很慢,一張圖片加載要半秒,10張圖塊就要耗費(fèi)5秒,影響用戶體驗(yàn)
package flyweight;/** *@Description 測(cè)試類 */ public class Client {private static void test1() {//在地圖第一行隨便繪制一些圖塊new Segment("河流", 10, 10).draw();new Segment("河流", 10, 20).draw();new Segment("道路", 10, 30).draw();new Segment("草地", 10, 40).draw();new Segment("草地", 10, 50).draw();new Segment("草地", 10, 60).draw();new Segment("草地", 10, 70).draw();new Segment("草地", 10, 80).draw();new Segment("道路", 10, 90).draw();new Segment("道路", 10, 100).draw();} }
- 圖片與坐標(biāo)狀態(tài)初始化后就固定下來(lái)了,簡(jiǎn)單講就是被繪制出來(lái)后就不必變動(dòng)了,即使要變也是將拼好的地圖作為一個(gè)大對(duì)象整體挪動(dòng)
- 圖件共享(優(yōu)化)
- 繼續(xù)分析每個(gè)圖塊的坐標(biāo)是不同的,但有很大一部分圖塊的材質(zhì)圖(圖片)是相同的
- 于是我們可以得出結(jié)論,材質(zhì)圖是可以作為享元的,而坐標(biāo)則不能
- 既然要共享相同的圖片,那么我們就得將圖塊類按圖片拆分成更細(xì)的材質(zhì)類,如河流類、草地類、道路類等
- 而坐標(biāo)不能作為圖塊類的享元屬性,所以我們就得設(shè)法把這個(gè)屬性抽離出去由外部負(fù)責(zé)
- 代碼實(shí)戰(zhàn)
- 首先需要定義一個(gè)接口,規(guī)范這些材質(zhì)類的繪圖標(biāo)準(zhǔn)(接口:規(guī)范標(biāo)準(zhǔn)Drawable)
- 當(dāng)然,除了接口方式,我們還可以用抽象類抽離出更多的屬性和方法,使子類變得更加簡(jiǎn)單
- 首先需要定義一個(gè)接口,規(guī)范這些材質(zhì)類的繪圖標(biāo)準(zhǔn)(接口:規(guī)范標(biāo)準(zhǔn)Drawable)
package flyweight; /** * @Description 繪圖接口: 規(guī)范這些材質(zhì)類的繪圖標(biāo)準(zhǔn) * 當(dāng)然,除了接口方式,我們還可以用抽象類抽離出更多的屬性和方法,使子類變得更加簡(jiǎn)單 */ public interface DrawAble { /** * 繪圖方法,接收地圖坐標(biāo) * @param x 橫坐標(biāo) * @param y 縱坐標(biāo) */ void draw(int x, int y); }
- 定義一系列材質(zhì)類并實(shí)現(xiàn)此繪圖接口
- 河流類River
package flyweight.texture; import flyweight.DrawAble; /*** @Description 河流類*/ public class River implements DrawAble {/*** 享元屬性: 河流圖片材質(zhì)作為內(nèi)部屬性*/private String image;/*** 類構(gòu)造器中加載河流圖片* 這就是類內(nèi)部即將共享的“元”數(shù)據(jù)了,我們通常稱之為“內(nèi)蘊(yùn)狀態(tài)”*/public River() {this.image = "河流";System.out.print("從磁盤(pán)加載[" + image + "]圖片,耗時(shí)……");}/*** 重寫(xiě)繪圖方法* 而作為“外蘊(yùn)狀態(tài)”的坐標(biāo)是無(wú)法作為享元的,由外部傳入* @param x 橫坐標(biāo)* @param y 縱坐標(biāo)*/@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上繪制圖片:[" + image + "]");} }
- 草地類Grass
package flyweight.texture;import flyweight.DrawAble;/*** @Description 草地類*/public class Grass implements DrawAble {/*** 享元屬性:草地圖片材質(zhì)*/private String image;public Grass() {this.image = "草地";System.out.print("從磁盤(pán)加載[" + image + "]圖片,耗時(shí)……");}@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上繪制圖片:[" + image + "]");}}
- 道路類Road
package flyweight.texture;import flyweight.DrawAble;/*** @Description 道路類*/public class Road implements DrawAble {/*** 道路圖片材質(zhì)*/private String image;public Road() {this.image = "道路";System.out.println("從磁盤(pán)加載[" + image + "]圖片,耗時(shí)…");}@Overridepublic void draw(int x, int y) {System.out.println("在位置[" + x + ":" + y + "]上繪制圖片:[" + image + "]");}}
- 房屋類House
package flyweight.texture;import flyweight.DrawAble;/*** @Description 房屋類*/public class House implements DrawAble {private String image;//房屋圖片材質(zhì)public House() {this.image = "房屋";System.out.print("從磁盤(pán)加載[" + image + "]圖片,耗時(shí)……");}@Overridepublic void draw(int x, int y) {System.out.print("將圖層切換到頂層……");//房屋蓋在地板上,所以切換到頂層圖層System.out.println("在位置[" + x + ":" + y + "]上繪制圖片:[" + image + "]");}}
- “元之共享”的關(guān)鍵:
- 定義一個(gè)圖件工廠類
package flyweight.factory;import flyweight.DrawAble;import flyweight.texture.Grass;import flyweight.texture.House;import flyweight.texture.River;import flyweight.texture.Road;import java.util.HashMap;import java.util.Map;/*** @Description 圖件工廠類*/public class SegmentFactory {/*** 圖庫(kù): 維護(hù)著所有的圖件元對(duì)象*/private Map<String, DrawAble> images;public SegmentFactory() {images = new HashMap<String, DrawAble>();}public DrawAble getDrawable(String image) {//緩存池里如果沒(méi)有圖件,則實(shí)例化并放入緩存池if(!images.containsKey(image)){switch (image) {case "河流":images.put(image, new River());break;case "草地":images.put(image, new Grass());break;case "道路":images.put(image, new Road());break;case "房屋":images.put(image, new House());}}//至此,緩存池里必然有圖件,直接取得并返回return images.get(image);}}
- 并將各種圖件對(duì)象提前放入內(nèi)存中共享,如此便可以避免每次從磁盤(pán)重新加載
- 測(cè)試:Client.test2()
private static void test2() {//先實(shí)例化圖件工廠SegmentFactory factory = new SegmentFactory();/** 隨便繪制一列為例:* 拋棄了利用“new”關(guān)鍵字隨意制造對(duì)象的方法,* 改用這個(gè)圖件工廠類來(lái)構(gòu)建并共享圖件元,外部需要什么圖件直接向圖件工廠索要即可*/factory.getDrawable("河流").draw(10, 10);factory.getDrawable("河流").draw(10, 20);factory.getDrawable("道路").draw(10, 30);factory.getDrawable("草地").draw(10, 40);factory.getDrawable("草地").draw(10, 50);factory.getDrawable("草地").draw(10, 60);factory.getDrawable("草地").draw(10, 70);factory.getDrawable("草地").draw(10, 80);factory.getDrawable("道路").draw(10, 90);factory.getDrawable("道路").draw(10, 100);//繪制完地板后接著在頂層繪制房屋factory.getDrawable("房子").draw(10, 10);factory.getDrawable("房子").draw(10, 50);}
- 小結(jié)
- 相同部分可以作為享元,如在構(gòu)造器中加載的,作為內(nèi)部類即將共享的元數(shù)據(jù),通常稱為“內(nèi)蘊(yùn)狀態(tài)”
- 不同部分不能作為享元,如在實(shí)現(xiàn)房中作為參數(shù)傳入的屬性,稱為“外蘊(yùn)狀態(tài)”
10.3 總結(jié)
- 享元模式讓圖件對(duì)象將可共享的內(nèi)蘊(yùn)狀態(tài)“圖片”維護(hù)起來(lái),將外蘊(yùn)狀態(tài)“坐標(biāo)”抽離出去并定義于接口參數(shù)中
- 基于此,享元工廠便可以順利將圖件對(duì)象共享,以供外部隨時(shí)使用。
- 享元模式的各角色定義如下
- Flyweight(享元接口):所有元件的高層規(guī)范,聲明與外蘊(yùn)狀態(tài)互動(dòng)的接口標(biāo)準(zhǔn)。如:DrawAble。
- ConcreteFlyweight(享元實(shí)現(xiàn)):
- 享元接口的元件實(shí)現(xiàn)類,自身維護(hù)著內(nèi)蘊(yùn)狀態(tài),且能接受并響應(yīng)外蘊(yùn)狀態(tài),
- 可以有多個(gè)實(shí)現(xiàn)。如:河流類River、草地類Grass、道路類Road等。
- 一個(gè)享元對(duì)象可以被稱作一個(gè)“元”
- FlyweightFactory(享元工廠):用來(lái)維護(hù)享元對(duì)象的工廠,負(fù)責(zé)對(duì)享元對(duì)象實(shí)例進(jìn)行創(chuàng)建與管理,并對(duì)外提供獲取享元對(duì)象的服務(wù)。SegmentFactory
- Client(客戶端):享元的使用者,負(fù)責(zé)維護(hù)外蘊(yùn)狀態(tài)。Client
- “享元”的理念其實(shí)就是萃取事物的本質(zhì)
- 將對(duì)象的內(nèi)蘊(yùn)狀態(tài)與外蘊(yùn)狀態(tài)剝離開(kāi)來(lái),其中內(nèi)蘊(yùn)狀態(tài)成為真正的“元”數(shù)據(jù),而外蘊(yùn)狀態(tài)則被抽離出去由外部負(fù)責(zé)維護(hù)