1. 程式人生 > >C++設計模式-Flyweight享元模式

C++設計模式-Flyweight享元模式

Flyweight享元模式

作用:運用共享技術有效地支援大量細粒度的物件。

內部狀態intrinsic和外部狀態extrinsic

1)Flyweight模式中,最重要的是將物件分解成intrinsic和extrinsic兩部分。

2)內部狀態:在享元物件內部並且不會隨環境改變而改變的共享部分,可以稱為是享元物件的內部狀態

3)外部狀態:而隨環境改變而改變的,取決於應用環境,或是實時資料,這些不可以共享的東西就是外部狀態了。

4)內部狀態和外部狀態之間的區別:
  在Flyweight模式應用中,通常修改的是外部狀態屬性,而內部狀態屬性一般都是用於參考或計算時引用。
Flyweight執行時所需的狀態必定是內部的或外部的。內部狀態儲存於ConcreteFlyweight物件之中;而外部狀態則由Client物件儲存或計算。當用戶呼叫Flyweight物件的操作時,將該狀態傳遞給它。

以文書處理軟體為例:

  內部狀態儲存於flyweight中,它包含了獨立於flyweight場景的資訊,這些資訊使得flyweight可以被共享。如字元程式碼,字元大小……

  外部狀態取決於flyweight場景,並根據場景而變化,因此不可共享。使用者物件負責在必要的時候將外部狀態傳遞給flyweight,如字元位置,字元顏色……

UML圖:


解析
Flyweight:享元類的基類,定義一個介面,通過這個介面Flyweight可以接受並作用於外部狀態。

ConcreteFlyweight:實現Flyweight介面, 併為內部狀態( 如果有的話) 增加儲存空間。ConcreteFlyweight物件必須是可共享的。它所儲存的狀態必須是內部的(intrinsic);即,它必須獨立於ConcreteFlyweight物件的場景。

UnsharedConcreteFlyweight:並非所有的Flyweight子類都需要被共享。Flyweight介面使共享成為可能,但它並不強制共享。在Flyweight物件結構的某些層次,UnsharedConcreteFlyweight物件通常將ConcreteFlyweight物件作為子節點。

FlyweightFactory

1) 建立並管理Flyweight物件。

2)確保合理地共享Flyweight。當用戶請求一個Flyweight時,FlyweightFactory物件提供一個已建立的例項或者建立一個(如果不存在的話)

Client
1)維持一個對Flyweight的引用。

2)計算或儲存一個(多個)Flyweight的外部狀態。

分析
   享元模式可以避免大量非常相似類的開銷。在程式設計中,有時需要生成大量細粒度的類例項來表示資料。如果能發現這些例項資料除了幾個引數外基本都是相同的。有時就能夠大幅度地減少例項化的類的數量。如果能把那些引數移到類例項的外面,在方法呼叫時將它們傳遞進來,就可以通過共享大幅度地減少單個例項的數目。

  比如在文件編輯器的設計過程中,我們如果為沒有字母建立一個物件的話,系統可能會因為大量的物件而造成儲存開銷的浪費。例如一個字母“a”在文件中出現了100000次,而實際上我們可以讓這一萬個字母“a”共享一個物件,當然因為在不同的位置可能字母“a”有不同的顯示效果(例如字型和大小等設定不同),在這種情況我們可以為將物件的狀態分為“外部狀態”和“內部狀態”,將可以被共享(不會變化)的狀態作為內部狀態儲存在物件中,而外部物件(例如上面提到的字型、大小等)我們可以在適當的時候將外部物件最為引數傳遞給物件(例如在顯示的時候,將字型、大小等資訊傳遞給物件)。

  Flyweight的內部狀態是用來共享的,Flyweightfactory負責維護一個Flyweight池(存放內部狀態的物件),當客戶端請求一個共享Flyweight時,這個factory首先搜尋池中是否已經有可適用的,如果有,factory只是簡單返回送出這個物件,否則,建立一個新的物件,加入到池中,再返回送出這個物件.池為重複或可共享的物件、屬性設定一個緩衝,稱為內部狀態。這些內部狀態一般情況下都是不可修改的,也就是在第一個物件、屬性被建立後,就不會去修改了(否則就沒意義了)。

  Flyweight 對物件的內部狀態進行共享,只為每種內部狀態建立一個例項,對內部狀態使用了單例模式。

  使用者不應直接對ConcreteFlyweight類進行例項化,而只能從FlyweightFactory物件得到ConcreteFlyweight物件,這可以保證對它們適當地進行共享。

  儲存節約由以下幾個因素決定:
  1) 因為共享,例項總數減少的數目
  2) 物件內部狀態的平均數目
  3) 外部狀態是計算的還是儲存的

實現要點
1)享元工廠維護一張享元例項表。

2)享元不可共享的狀態需要在外部維護。即刪除外部狀態:該模式的可用性在很大程度上取決於是否容易識別外部狀態並將它從共享物件中刪除。

3)按照需求可以對享元角色進行抽象。

4)管理共享物件:引用計數和垃圾回收……

何時採用
1)如果一個應用程式使用了大量的物件,而大量的這些物件造成了很大的儲存開銷時就應該考慮使用;

2)還有就是物件的大多數狀態可變為外部狀態,如果刪除物件的外部狀態,那麼可以用相對較少的共享物件取代很多組物件,此時可以考慮所使用享元模式。

3)系統中有大量耗費了大量記憶體的細粒度物件,並且對外界來說這些對沒有任何差別的(或者說經過改造後可以是沒有差別的)。

  在文件編輯器例子中如果一個字元對應一個物件,那麼一篇文件所要容納的物件將是非常的龐大耗費大量的記憶體。而實際組成文件的字元是有限的,是由這些字元不同的組合和排列得到的。所以需要共享,將基本的字元進行共享,將使得字元物件變得有限。

程式碼如下:

Flyweight.h

複製程式碼
 1 #ifndef _FLYWEIGHT_H_
 2 #define _FLYWEIGHT_H_
 3 
 4 #include <string>
 5 #include <vector>
 6 
 7 using namespace std;
 8 
 9 //基類,定義操作介面Operation
10 class Flyweight
11 {
12 public:
13     //操作外部狀態extrinsicState
14     virtual void Operation(const string& extrinsicState)=0;
15     string GetIntrinsicState();
16     virtual ~Flyweight();
17 protected:
18     Flyweight(string intrinsicState);
19 private:
20     //內部狀態,也可以放在ConcreteFlyweight中
21     string _intrinsicState;
22 };
23 
24 class ConcreteFlyweight:public Flyweight
25 {
26 public:
27     //實現介面函式
28     virtual void Operation(const string& extrinsicState);
29     ConcreteFlyweight(string intrinsicState);
30     ~ConcreteFlyweight();
31 };
32 
33 class UnsharedConcreteFlyweight:public Flyweight
34 {
35 public:
36     virtual void Operation(const string& extrinsicState);
37     UnsharedConcreteFlyweight(string intrinsicState);
38     ~UnsharedConcreteFlyweight();
39 };
40 
41 class FlyweightFactory
42 {
43 public:
44     FlyweightFactory();
45     ~FlyweightFactory();
46     //獲得一個請求的Flyweight物件
47     Flyweight* GetFlyweight(string key);
48     //獲取容器中儲存的物件數量
49     void GetFlyweightCount();
50 protected:
51 private:
52     //儲存內部狀態物件的容器
53     vector<Flyweight*> m_vecFly;
54 };
55 #endif
複製程式碼

Flyweight.cpp

複製程式碼
 1 #include "Flyweight.h"
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 Flyweight::Flyweight(string intrinsicState)
 7 {
 8     this->_intrinsicState = intrinsicState;
 9 }
10 
11 Flyweight::~Flyweight()
12 {}
13 
14 string Flyweight::GetIntrinsicState()
15 {
16     return this->_intrinsicState;
17 }
18 
19 ConcreteFlyweight::ConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)
20 {
21 }
22 
23 ConcreteFlyweight::~ConcreteFlyweight()
24 {
25 }
26 
27 void ConcreteFlyweight::Operation(const string& extrinsicState)
28 {
29     cout << this->GetIntrinsicState() << endl;
30     cout << extrinsicState << endl;
31 }
32 
33 UnsharedConcreteFlyweight::UnsharedConcreteFlyweight(string intrinsicState):Flyweight(intrinsicState)
34 {
35 }
36 
37 UnsharedConcreteFlyweight::~UnsharedConcreteFlyweight()
38 {
39 }
40 
41 void UnsharedConcreteFlyweight::Operation(const string& extrinsicState)
42 {
43     cout << "extrinsicState" << endl;
44 }
45 
46 FlyweightFactory::FlyweightFactory()
47 {}
48 
49 FlyweightFactory::~FlyweightFactory()
50 {}
51 
52 //若該物件已存在,直接返回,否則新建一個物件,存入容器中,再返回
53 Flyweight* FlyweightFactory::GetFlyweight(string key)
54 {
55     vector<Flyweight*>::iterator iter = this->m_vecFly.begin();
56     for(;iter!= this->m_vecFly.end();iter++)
57     {
58         if((*iter)->GetIntrinsicState() == key)
59         {
60             return *iter;
61         }
62     }
63     Flyweight* fly = new ConcreteFlyweight(key);
64     this->m_vecFly.push_back(fly);
65     return fly;
66 }
67 
68 void FlyweightFactory::GetFlyweightCount()
69 {
70     cout << this->m_vecFly.size() << endl;
71 }
複製程式碼

main.cpp

複製程式碼
 1 #include "Flyweight.h"
 2 #include <iostream>
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 int main()
 8 {
 9     //外部狀態extrinsicState
10     string extrinsicState = "ext";
11 
12     //工廠物件,工廠物件
13     FlyweightFactory* fc = new FlyweightFactory();
14 
15     //向工廠申請一個Flyweight物件,且該物件的內部狀態值為“hello”
16     Flyweight* fly = fc->GetFlyweight("hello");
17 
18     Flyweight* fly1 = fc->GetFlyweight("hello");
19 
20     //應用外部狀態
21     fly->Operation(extrinsicState);
22 
23     fc->GetFlyweightCount();
24 
25     return 0;
26 }
複製程式碼

另外一個文字處理的例子

參考:

程式碼如下:

Document.h

複製程式碼
 1 #ifndef _DOCUMENT_H_
 2 #define _DOCUMENT_H_
 3 
 4 #include <string>
 5 #include <vector>
 6 
 7 using namespace std;
 8 
 9 // The 'Flyweight' abstract class
10 class character
11 {
12 public:
13     //解構函式
14     virtual ~character();
15     //應用外部狀態
16     virtual void Display(int width,int height,int ascent,int descent,int pointSize)=0;
17     //獲取內部狀態
18     virtual char GetSymbol()=0;
19 protected:
20     /*-----內部狀態---------*/
21     char symbol;
22     /*----------------------/
23 
24     /*-----外部狀態---------*/
25     int width;
26     int height;
27     int ascent;
28     int descent;
29     int pointSize;
30     /*----------------------*/
31     //建構函式
32     character(char c);
33 };
34 
35 //A 'ConcreteFlyweight' class
36 class characterA:public character
37 {
38 public:
39     characterA(char c);
40     ~characterA();
41     virtual void Display(int width,int height,int ascent,int descent,int pointSize);
42     virtual char GetSymbol();
43 protected:
44 private:
45 };
46 
47 //B 'ConcreteFlyweight' class
48 class characterB:public character
49 {
50 public:
51     characterB(char c);
52     ~characterB();
53     virtual void Display(int width,int height,int ascent,int descent,int pointSize);
54 protected:
55 private:
56 };
57 
58 //C 'ConcreteFlyweight' class
59 class characterC:public character
60 {
61 public:
62     characterC(char c);
63     ~characterC();
64     virtual void Display(int width,int height,int ascent,int descent,int pointSize);
65 protected:
66 private:
67 };
68 
69 //D 'ConcreteFlyweight' class
70 class characterD:public character
71 {
72 public:
73     characterD(char c);
74     ~characterD();
75     virtual void Display(int width,int height,int ascent,int descent,int pointSize);
76 protected:
77 private:
78 };
79 /*
80 ...
81 */
82 
83 //The 'FlyweightFactory' class
84 class characterFactory
85 {
86 public:
87     characterFactory();
88     ~characterFactory();
89     //申請一個character物件
90     character* GetCharacter(char);
91     //獲取儲存的character*數量
92     vector<character*>::size_type GetCount();
93 private:
94     //儲存character*的容器,可以換成List等其它容器
95     vector<character*> m_vecCharacter;
96 };
97 
98 #endif
複製程式碼

Document.cpp

複製程式碼
 1 #include "Document.h"
 2 #include <iostream>
 3 
 4 character::character(char c)
 5 {
 6     this->symbol = c;
 7 }
 8 
 9 character::~character()
10 {
11 }
12 
13 characterA::characterA(char c):character(c)
14 {
15 }
16 
17 characterA::~characterA()
18 {
19 }
20 
21 char characterA::GetSymbol()
22 {
23     return this->symbol;
24 }
25 
26 void characterA::Display(int width,int height,int ascent,int descent,int pointSize)
27 {
28     //接收並作用外部狀態
29     this->ascent = ascent;
30     this->descent = descent;
31     this->height = height;
32     this->pointSize = pointSize;
33     this->width = width;
34 
35     cout << this->symbol <<" "
36      << this->ascent <<" "
37      << this->descent <<" "
38      << this->height <<" "
39      << this->pointSize <<" "
40      << this->width << endl;
41 }
42 
43 characterFactory::characterFactory()
44 {}
45 
46 characterFactory::~characterFactory()
47 {}
48 
49 character* characterFactory::GetCharacter(char c)
50 {
51     vector<character*>::iterator iter = this->m_vecCharacter.begin();
52     for(;iter != this->m_vecCharacter.end();iter++)
53     {
54         if((*iter)->GetSymbol() == c)
55         {
56             return *iter;
57         }
58     }
59     character* pf = new characterA(c);
60     this->m_vecCharacter.push_back(pf);
61     return pf;
62 }
63 
64 vector<character*>::size_type characterFactory::GetCount()
65 {
66     return this->m_vecCharacter.size();
67 }
複製程式碼