java設計模式——結構型之享元模式


自大學課程初識設計模式以來,就越發覺得有必要系統學習一下設計模式。
剛好在實習前准備期間課比較少,抽出一點時間整理一下記一些筆記,復制粘貼比較多。
筆記比較適合學習過設計模式的同學。

Flyweight Pattern(享元模式)

學習鏈接:極客學院Wiki_Java設計模式之結構型模式

另外感謝劉偉博士,學習設計模式可以看劉偉博士的博客,很詳盡。

劉偉技術博客


享元模式的適用范圍

(1) 一個系統有大量相同或者相似的對象需要被創建,造成內存的大量耗費。

(2) 對象的大部分狀態都可以外部化,可以將這些外部狀態(方法的參數)傳入對象中。

(3) 在使用享元模式時需要維護一個存儲享元對象的享元池,而這需要耗費一定的系統資源,因此,應當在需要多次重復使用享元對象時才值得使用享元模式。


應用實例

在Java中的String就采取了享元模式,賦值時為同一個字符串時創建的是同一個對象。


享元模式如何實現

角色

Flyweight(抽象享元類)

通常是一個接口或抽象類,在抽象享元類中聲明了具體享元類公共的方法,這些方法可以向外界提供享元對象的內部數據(內部狀態),同時也可以通過這些方法來設置外部數據(外部狀態)。

ConcreteFlyweight(具體享元類)

它實現了抽象享元類,其實例稱為享元對象;在具體享元類中為內部狀態提供了存儲空間。通常我們可以結合單例模式來設計具體享元類,為每一個具體享元類提供唯一的享元對象。

UnsharedConcreteFlyweight(非共享具體享元類)

並不是所有的抽象享元類的子類都需要被共享,不能被共享的子類可設計為非共享具體享元類;當需要一個非共享具體享元類的對象時可以直接通過實例化創建。

FlyweightFactory(享元工廠類)

享元工廠類用於創建並管理享元對象,它針對抽象享元類編程,將各種類型的具體享元對象存儲在一個享元池中,享元池一般設計為一個存儲“鍵值對”的集合(也可以是其他類型的集合),可以結合工廠模式進行設計;當用戶請求一個具體享元對象時,享元工廠提供一個存儲在享元池中已創建的實例或者創建一個新的實例(如果不存在的話),返回新創建的實例並將其存儲在享元池中。

類圖

這里寫圖片描述
享元模式有結合簡單工廠模式,使用工廠模式就能封裝創建過程,客戶端就無法控制創建,達到返回相同實例的目的。另外抽象享元類也可以被實現為無法共享的類。

另外還有復合享元類,目前還不是很懂這個可以拿來干嘛。。。
這里寫圖片描述


享元模式的優缺點

主要優點

(1) 可以極大減少內存中對象的數量,使得相同或相似對象在內存中只保存一份,從而可以節約系統資源,提高系統性能。

(2) 享元模式的外部狀態相對獨立,而且不會影響其內部狀態,從而使得享元對象可以在不同的環境中被共享。

主要缺點

(1) 享元模式使得系統變得復雜,需要分離出內部狀態和外部狀態,這使得程序的邏輯復雜化。

(2) 為了使對象可以共享,享元模式需要將享元對象的部分狀態外部化,而讀取外部狀態將使得運行時間變長。


練習

題目

Sunny 軟件公司欲開發一個多功能文檔編輯器,在文本文檔中可以插入圖片、動畫、視頻等多媒體資料,為了節約系統資源,相同的圖片、動畫和視頻在同一個文檔中只需保存一份,但是可以多次重復出現,而且它們每次出現時位置和大小均可不同。試使用享元模式設計該文檔編輯器。

題目分析

題目中出現的圖片、動畫、視頻可以看做是具體享元類,為了簡便假定他們的地址為內部狀態,它們的大小和位置為外部狀態。

實現代碼

抽象享元類:Flyweight.java

package com.joy;

public abstract class Flyweight {

public abstract String getMessage();

public void display(int length, int high, int i, int j) {
System.out.println("在(" + i + "," + j + ")獲得一個" + length + "cm*" + high + "cm的" + this.getMessage());
}
}

具體享元類:Picture.java

package com.joy;

public class Picture extends Flyweight {

private String address;
public Picture(String address){
this.address = address;
}
@Override
public String getMessage() {
return address+".jpg";
}

}

具體享元類:GIF.java

package com.joy;

public class GIF extends Flyweight {

private String address;
public GIF(String address){
this.address = address;
}
@Override
public String getMessage() {
return address+".gif";
}

}

具體享元類:Video.java

package com.joy;

public class Video extends Flyweight {

private String address;
public Video(String address){
this.address = address;
}
@Override
public String getMessage() {
return address+".avi";
}

}

享元工廠類:FlyweightFactory.java

package com.joy;

import java.util.HashMap;

public class FlyweightFactory {

private static FlyweightFactory ff = new FlyweightFactory();
private static HashMap<String, Flyweight> ht;

private FlyweightFactory() {
ht = new HashMap<String, Flyweight>();
}

public static FlyweightFactory getInstance() {
return ff;
}

public static Flyweight getFlyweight(String address) throws Exception {
Flyweight appendix;
String trueAddr = address.split("\\.")[0];//使用轉譯字符
String type = address.split("\\.")[1];
if (!ht.containsKey(address)){
if (type.equals("jpg")) {
appendix = new Picture(trueAddr);
ht.put(address, appendix);
}
else if (type.equals("gif")) {
appendix = new GIF(trueAddr);
ht.put(address, appendix);
}
else if (type.equals("avi")) {
appendix = new Video(trueAddr);
ht.put(address, appendix);
}
}
return ht.get(address);
}
}

客戶端:Clien.java

package com.joy;

public class Client {

public static void main(String[] args) throws Exception {

Flyweight f1,f2,f3,f4;
f1 = FlyweightFactory.getFlyweight("hello.jpg");
f1.display(300, 400, 0, 0);
f2 = FlyweightFactory.getFlyweight("hello.jpg");
System.out.println("f1,f2是否為同一對象:"+(f1==f2));
f3 = FlyweightFactory.getFlyweight("cat.gif");
f3.display(100, 100, 600, 500);
f4 = FlyweightFactory.getFlyweight("dog.avi");
f4.display(600, 500, 0, 0);

}

}

運行結果

這里寫圖片描述


總結

享元模式可能不太常用,在理解的時候需要深入理解內部狀態和外部狀態的意義。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: