摘要:
# 物件導向技術可以很好地解決一些彈性或可擴展性問題,但在許多情況下需要在系統中增加類別和物件的數量。當物件數量太多時,將導致運行代價過高,帶來效能下降等問題。 享元模式透過共享技術實現相同或相似物件的重複使用來提高系統資源的使用率。本文首先闡述了享元模式要解決的問題和解決問題的概念,然後從實現角度重點說明了該模式的本質,並進一步給出了其所包含的角色和組織結構。最後,給出了共享模式的應用實例和使用場景。
#物件導向技術可以很好地解決一些彈性或可擴充性問題,但在許多情況下需要在系統中增加類別和物件的個數。當物件數量太多時,將導致運行代價過高,帶來效能下降等問題。享元模式正是為解決這一類問題而誕生的。 享元模式透過共享技術實現相同或相似對象的重用,示意圖如下(我們可以共用一個Hello world 對象,其中字符串 “Hello world” 為內部狀態,可共享;字體顏色為外部狀態,不可共享,由客戶端設定):
# # # #」 # 」。 內部狀態(Intrinsic State),而那些需要外部環境來設定的不能共享的內容稱為
#外部狀態(Extrinsic State),其中外部狀態和內部狀態是相互獨立的,外部狀態的變化不會引起內部狀態的變化。 由於區分了內部狀態和外部狀態,因此可以透過設定不同的外部狀態使得相同的物件可以具有一些不同的特徵,而相同的內部狀態是可以共享的。 也就是說,
享元模式的本質是分離與共享 : 分離變與不變,並且共享不變。 把一個物件的狀態分成內部狀態和外部狀態,內部狀態即是不變的,外部狀態是變化的;然後透過共享不變的部分,達到減少物件數量並節省記憶體的目的。
細粒度物件。 享元模式的目的是使用共享技術來實現大量細粒度物件的重複使用。
二. 定義與結構 享元模式(Flyweight Pattern):運用共享技術有效地支援大量細粒度物件的複用。系統只使用少量的對象,而這些對像都很相似,狀態變化很小,可以實現對象的多次復用。由於享元模式要求能夠共享的對象必須是細粒度對象,因此它又稱為輕量級模式,它是一種 對象結構型模式
Flyweight: 享元介面,透過這個介面傳入外部狀態並作用於外部狀態;
ConcreteFlyweight: 具體的享元實現對象,必須是可共享的,需要封裝享元對象的內部狀態;
UnsharedConcreteFlyweight: 非共享的享元實現對象,並不是所有的享元對像都可以共享,非共享的享元物件通常是享元物件的組合物件;
FlyweightFactory: 享元工廠,主要用來建立並管理共享的享元對象,並對外提供存取共享享元的介面;
2、模式結構
3、模式擴展
單純享元模式:在單純享元模式中,所有的享元物件都是可以共享的,也就是所有抽象享元類別的子類別都可共享,不存在非共享具體享元類別。
實作範例:
//抽象享元角色类public interface Flyweight { //一个示意性方法,参数state是外蕴状态 public void operation(String state); }//具体享元角色类//具体享元角色类ConcreteFlyweight有一个内蕴状态,在本例中一个Character类型的intrinsicState属性代表,它的值应当在享元对象 //被创建时赋予。所有的内蕴状态在对象创建之后,就不会再改变了。如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端, //在使用享元对象时,再由客户端传入享元对象。这里只有一个外蕴状态,operation()方法的参数state就是由外部传入的外蕴状态。 public class ConcreteFlyweight implements Flyweight { private Character intrinsicState = null; /** * 构造函数,内蕴状态作为参数传入 * @param state */ public ConcreteFlyweight(Character state){ this.intrinsicState = state; } /** * 外蕴状态作为参数传入方法中,改变方法的行为, * 但是并不改变对象的内蕴状态。 */ @Override public void operation(String state) { // TODO Auto-generated method stub System.out.println("Intrinsic State = " + this.intrinsicState); System.out.println("Extrinsic State = " + state); } }//享元工厂角色类//享元工厂角色类,必须指出的是,客户端不可以直接将具体享元类实例化,而必须通过一个工厂对象,利用一个factory()方法得到享元对象。 //一般而言,享元工厂对象在整个系统中只有一个,因此也可以使用单例模式。 //当客户端需要单纯享元对象的时候,需要调用享元工厂的factory()方法,并传入所需的单纯享元对象的内蕴状态,由工厂方法产生所需要的//享元对象。 public class FlyweightFactory { private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>(); public Flyweight factory(Character state){ //先从缓存中查找对象 Flyweight fly = files.get(state); if(fly == null){ //如果对象不存在则创建一个新的Flyweight对象 fly = new ConcreteFlyweight(state); //把这个新的Flyweight对象添加到缓存中 files.put(state, fly); } return fly; } }//客户端类public class Client { public static void main(String[] args) { // TODO Auto-generated method stub FlyweightFactory factory = new FlyweightFactory(); Flyweight fly = factory.factory(new Character('a')); fly.operation("First Call"); fly = factory.factory(new Character('b')); fly.operation("Second Call"); fly = factory.factory(new Character('a')); fly.operation("Third Call"); } }
雖然讓客戶端創建了三個物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元物件,但是只有實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是實際創建的元對象,但是兩個,這就是共享的意思。 運行結果如下:
。加以組合,可以形成複合享元對象,這樣的複合享元對象本身不能共享,但是它們可以分解成單純享元對象,而後者則可以共享。
實作範例://抽象享元角色类public interface Flyweight {
//一个示意性方法,参数state是外蕴状态
public void operation(String state);
}//具体享元角色类
//具体享元角色类ConcreteFlyweight有一个内蕴状态,在本例中一个Character类型的intrinsicState属性代表,它的值应当在享元对象
//被创建时赋予。所有的内蕴状态在对象创建之后,就不会再改变了。如果一个享元对象有外蕴状态的话,所有的外部状态都必须存储在客户端,
//在使用享元对象时,再由客户端传入享元对象。这里只有一个外蕴状态,operation()方法的参数state就是由外部传入的外蕴状态。
public class ConcreteFlyweight implements Flyweight {
private Character intrinsicState = null; /**
* 构造函数,内蕴状态作为参数传入
* @param state
*/
public ConcreteFlyweight(Character state){ this.intrinsicState = state;
} /**
* 外蕴状态作为参数传入方法中,改变方法的行为,
* 但是并不改变对象的内蕴状态。
*/
@Override
public void operation(String state) { // TODO Auto-generated method stub
System.out.println("Intrinsic State = " + this.intrinsicState);
System.out.println("Extrinsic State = " + state);
}
}
//复合享元角色类
//复合享元对象是由单纯享元对象通过复合而成的,因此它提供了add()这样的聚集管理方法。由于一个复合享元对象具有不同的聚集元素,
//这些聚集元素在复合享元对象被创建之后加入,这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。
//复合享元角色实现了抽象享元角色所规定的接口,也就是operation()方法,这个方法有一个参数,代表复合享元对象的外蕴状态。
//一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的;
//而一个复合享元对象所含有的单纯享元对象的内蕴状态一般是不相等的,不然就没有使用价值了。
public class ConcreteCompositeFlyweight implements Flyweight {
private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>(); /**
* 增加一个新的单纯享元对象到聚集中
*/
public void add(Character key , Flyweight fly){
files.put(key,fly);
} /**
* 外蕴状态作为参数传入到方法中
*/
@Override
public void operation(String state) {
Flyweight fly = null; for(Object o : files.keySet()){
fly = files.get(o);
fly.operation(state);
}
}
}//享元工厂角色类//享元工厂角色提供两种不同的方法,一种用于提供单纯享元对象,另一种用于提供复合享元对象。public class FlyweightFactory {
private Map<Character,Flyweight> files = new HashMap<Character,Flyweight>(); /**
* 复合享元工厂方法
*/
public Flyweight factory(List<Character> compositeState){
ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight(); for(Character state : compositeState){
compositeFly.add(state,this.factory(state));
} return compositeFly;
} /**
* 单纯享元工厂方法
*/
public Flyweight factory(Character state){ //先从缓存中查找对象
Flyweight fly = files.get(state); if(fly == null){ //如果对象不存在则创建一个新的Flyweight对象
fly = new ConcreteFlyweight(state); //把这个新的Flyweight对象添加到缓存中
files.put(state, fly);
} return fly;
}
}//客户端类public class Client {
public static void main(String[] args) {
List<Character> compositeState = new ArrayList<Character>();
compositeState.add('a');
compositeState.add('b');
compositeState.add('c');
compositeState.add('a');
compositeState.add('b');
FlyweightFactory flyFactory = new FlyweightFactory();
Flyweight compositeFly1 = flyFactory.factory(compositeState);
Flyweight compositeFly2 = flyFactory.factory(compositeState);
compositeFly1.operation("Composite Call");
System.out.println("---------------------------------");
System.out.println("复合享元模式是否可以共享对象:" + (compositeFly1 == compositeFly2));
Character state = 'a';
Flyweight fly1 = flyFactory.factory(state);
Flyweight fly2 = flyFactory.factory(state);
System.out.println("单纯享元模式是否可以共享对象:" + (fly1 == fly2));
}
}
的工廠方法用於返回享元對象,使用 簡單工廠模式來產生享元對象#;
在一個系統中,通常只有唯一一個享元工廠,因此享元工廠類別可以使用單例模式進行設計;#
享元模式可以結合組合模式形成複合享元模式,統一對享元對象設定外部狀態。
三.模式分析
# 考慮系統效能的 設計模式
,透過使用享元模式可以節省記憶體空間,提高系統的效能。#享元模式的核心在於享元工廠類,享元工廠類的作用在於提供一個用於存儲享元對象的享元池,用戶需要物件時,首先從享元池中獲取,如果享元池中不存在,則創建一個新的享元物件返回給用戶,並在享元池中保存該
public class FlyweightFactory{ private HashMap flyweights = new HashMap(); public Flyweight getFlyweight(String key) { if(flyweights.containsKey(key)) { return (Flyweight)flyweights.get(key); } else { Flyweight fw = new ConcreteFlyweight(); flyweights.put(key,fw); return fw; } } }
public class Flyweight{ //内部状态作为成员属性 private String intrinsicState; public Flyweight(String intrinsicState) { this.intrinsicState = intrinsicState; } public void operation(String extrinsicState) { ...... } }
享元模式在编辑器软件中大量使用,如在一个文档中多次出现相同的图片,则只需要创建一个图片对象,通过在应用程序中设置该图片出现的位置,可以实现该图片在不同地方多次重复显示。
广义上讲,在JDK类库中定义的String类也是使用享元模式的典型。
public class Demo{ public static void main(String args[]) { String str1 = "abcd"; String str2 = "abcd"; String str3 = "ab" + "cd"; String str4 = "ab"; str4 += "cd"; System.out.println(str1 == str2); //true System.out.println(str1 == str3); //true System.out.println(str1 == str4); //false } }
1、模式适用环境
在以下情况下可以使用享元模式:
一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费;
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中(细粒度对象);
使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。
2、模式的优点
(1)它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份;
(2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
3、模式的缺点
(1)享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化;
(2)为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
4、模式的实现
享元模式运用共享技术有效地支持大量 细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用,它是一种对象结构型模式。
享元模式包含四个角色:抽象享元类声明一个接口,通过它可以接受并作用于外部状态;具体享元类实现了抽象享元接口,其实例称为享元对象;非共享具体享元是不能被共享的抽象享元类的子类;享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中。
享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态和外部状态。其中内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享;外部状态是随环境改变而改变的、不可以共享的状态。
以上是深入理解享元模式範例程式碼(圖文)的詳細內容。更多資訊請關注PHP中文網其他相關文章!