C++設計形式之享元形式。本站提示廣大學習愛好者:(C++設計形式之享元形式)文章只能為提供參考,不一定能成為您想要的結果。以下是C++設計形式之享元形式正文
媒介
無聊的時刻,也去QQ游戲年夜廳玩五子棋或許象棋;作為法式員,看到一個產物,總要去想一想它是怎樣設計的,怎樣完成的,我想這個是一切法式員都邑做的工作吧(強制症???)。有的時刻,想完了,還要做一個DEMO出來,能力表現本身的NB,然後還有點小造詣感。
在玩五子棋或象棋的時刻,我就想過,騰訊那幫店員是怎樣做的呢?五子棋的棋子有诟谇兩色,豈非每次放一個棋子就new一個對象麼?象棋有車、馬、相、士、帥、炮和兵,是否是每盤棋都要把一切的棋子都new出來呢?假如真的是每個棋子都new一個,那末再加上那末多人玩;那要new若干對象啊,假如是如許做的話,我想有若干辦事器都是弄不定的,能夠QQ游戲年夜廳會比12306還蹩腳。那騰訊那幫店員是若何完成的呢?那就要說到明天總結的享元形式了。
甚麼是享元形式?
在GOF的《設計形式:可復用面向對象軟件的基本》一書中對享元形式是如許說的:應用同享技巧有用地支撐年夜量細粒度的對象。
就如下面說的棋子,假如每一個棋子都new一個對象,就會存在年夜量細粒度的棋子對象,這對辦事器的內存空間是一種考驗,也是一種糟蹋。我們都曉得,好比我在2013號房間和他人下五子棋,2014號房間也有人鄙人五子棋,其實不會由於我在2013號房間,而他人在2014號房間,而招致我們的棋子是紛歧樣的。這就是說,2013號房間和2014號房間的棋子都是一樣的,一切的五子棋房間的棋子都是一樣的。獨一的分歧是每一個棋子在分歧的房間的分歧棋盤的分歧地位上。所以,關於棋子來講,我們不消放一個棋子就new一個棋子對象,只須要在須要的時刻,去要求取得對應的棋子對象,假如沒有,就new一個棋子對象;假如有了,就直接前往棋子對象。這裡以五子棋為例子,停止剖析,當玩家在棋盤上放入第一個白色棋子時,此時因為沒有白色棋子,所以就new一個白色棋子;當另外一個玩家放入第一個黑色棋子時,此時因為沒有黑色棋子,所以就須要new一個黑色棋子;當玩家再次放入一個白色棋子時,就去查詢能否有曾經存在的白色棋子對象,因為第一次曾經new了一個白色棋子對象,所以,如今不會再次new一個白色棋子對象,而是前往之前new的白色棋子對象;關於黑色棋子,亦是同理;取得了棋子對象,我們只須要設置棋子的分歧棋盤地位便可。
UML類圖
Flyweight:描寫一個接口,經由過程這個接口flyweight可以接收並感化於內部狀況;
ConcreteFlyweight:完成Flyweight接口,並為界說了一些外部狀況,ConcreteFlyweight對象必需是可同享的;同時,它所存儲的狀況必需是外部的;即,它必需自力於ConcreteFlyweight對象的場景;
UnsharedConcreteFlyweight:並不是一切的Flyweight子類都須要被同享。Flyweight接口使同享成為能夠,但它其實不強迫同享。
FlyweightFactory:創立並治理flyweight對象。它須要確保公道地同享flyweight;當用戶要求一個flyweight時,FlyweightFactory對象供給一個已創立的實例,假如要求的實例不存在的情形下,就新創立一個實例;
Client:保持一個對flyweight的援用;同時,它須要盤算或存儲flyweight的內部狀況。
完成要點
依據我們的經歷,當要將一個對象停止同享時,就須要斟酌到對象的狀況成績了;分歧的客戶端取得同享的對象以後,能夠會修正同享對象的某些狀況;年夜家都修正了同享對象的狀況,那末就會湧現對象狀況的雜亂。關於享元形式,在完成時必定要斟酌到同享對象的狀況成績。那末享元形式是若何完成的呢?
在享元形式中,有兩個異常主要的概念:外部狀況和內部狀況。
外部狀況存儲於flyweight中,它包括了自力於flyweight場景的信息,這些信息使得flyweight可以被同享。而內部狀況取決於flyweight場景,並依據場景而變更,是以弗成同享。用戶對象擔任在需要的時刻將內部狀況傳遞給flyweight。
flyweight履行時所需的狀況一定是外部的或內部的。外部狀況存儲於ConcreteFlyweight對象當中;而內部對象則由Client對象存儲或盤算。當用戶挪用flyweight對象的操作時,將該狀況傳遞給它。同時,用戶不該該直接對ConcreteFlyweight類停止實例化,而只能從FlyweightFactory對象獲得ConcreteFlyweight對象,這可以包管對它們恰當地停止同享;因為同享一個實例,所以在創立這個實例時,便可以斟酌應用單例形式來停止完成。
享元形式的工場類保護了一個實例列表,這個列表中保留了一切的同享實例;當用戶從享元形式的工場類要求同享對象時,起首查詢這個實例表,假如不存在對應實例,則創立一個;假如存在,則直接前往對應的實例。
代碼完成:
#include <iostream>
#include <map>
#include <vector>
using namespace std;
typedef struct pointTag
{
int x;
int y;
pointTag(){}
pointTag(int a, int b)
{
x = a;
y = b;
}
bool operator <(const pointTag& other) const
{
if (x < other.x)
{
return true;
}
else if (x == other.x)
{
return y < other.y;
}
return false;
}
}POINT;
typedef enum PieceColorTag
{
BLACK,
WHITE
}PIECECOLOR;
class CPiece
{
public:
CPiece(PIECECOLOR color) : m_color(color){}
PIECECOLOR GetColor() { return m_color; }
// Set the external state
void SetPoint(POINT point) { m_point = point; }
POINT GetPoint() { return m_point; }
protected:
// Internal state
PIECECOLOR m_color;
// external state
POINT m_point;
};
class CGomoku : public CPiece
{
public:
CGomoku(PIECECOLOR color) : CPiece(color){}
};
class CPieceFactory
{
public:
CPiece *GetPiece(PIECECOLOR color)
{
CPiece *pPiece = NULL;
if (m_vecPiece.empty())
{
pPiece = new CGomoku(color);
m_vecPiece.push_back(pPiece);
}
else
{
bool bFound = false; // 異常感激fireace指出的成績
for (vector<CPiece *>::iterator it = m_vecPiece.begin(); it != m_vecPiece.end(); ++it)
{
if ((*it)->GetColor() == color)
{
bFound = true;
pPiece = *it;
break;
}
bFound = false;
}
if (!bFound)
{
pPiece = new CGomoku(color);
m_vecPiece.push_back(pPiece);
}
}
return pPiece;
}
~CPieceFactory()
{
for (vector<CPiece *>::iterator it = m_vecPiece.begin(); it != m_vecPiece.end(); ++it)
{
if (*it != NULL)
{
delete *it;
*it = NULL;
}
}
}
private:
vector<CPiece *> m_vecPiece;
};
class CChessboard
{
public:
void Draw(CPiece *piece)
{
if (piece->GetColor())
{
cout<<"Draw a White"<<" at ("<<piece->GetPoint().x<<","<<piece->GetPoint().y<<")"<<endl;
}
else
{
cout<<"Draw a Black"<<" at ("<<piece->GetPoint().x<<","<<piece->GetPoint().y<<")"<<endl;
}
m_mapPieces.insert(pair<POINT, CPiece *>(piece->GetPoint(), piece));
}
void ShowAllPieces()
{
for (map<POINT, CPiece *>::iterator it = m_mapPieces.begin(); it != m_mapPieces.end(); ++it)
{
if (it->second->GetColor())
{
cout<<"("<<it->first.x<<","<<it->first.y<<") has a White chese."<<endl;
}
else
{
cout<<"("<<it->first.x<<","<<it->first.y<<") has a Black chese."<<endl;
}
}
}
private:
map<POINT, CPiece *> m_mapPieces;
};
int main()
{
CPieceFactory *pPieceFactory = new CPieceFactory();
CChessboard *pCheseboard = new CChessboard();
// The player1 get a white piece from the pieces bowl
CPiece *pPiece = pPieceFactory->GetPiece(WHITE);
pPiece->SetPoint(POINT(2, 3));
pCheseboard->Draw(pPiece);
// The player2 get a black piece from the pieces bowl
pPiece = pPieceFactory->GetPiece(BLACK);
pPiece->SetPoint(POINT(4, 5));
pCheseboard->Draw(pPiece);
// The player1 get a white piece from the pieces bowl
pPiece = pPieceFactory->GetPiece(WHITE);
pPiece->SetPoint(POINT(2, 4));
pCheseboard->Draw(pPiece);
// The player2 get a black piece from the pieces bowl
pPiece = pPieceFactory->GetPiece(BLACK);
pPiece->SetPoint(POINT(3, 5));
pCheseboard->Draw(pPiece);
/*......*/
//Show all cheses
cout<<"Show all cheses"<<endl;
pCheseboard->ShowAllPieces();
if (pCheseboard != NULL)
{
delete pCheseboard;
pCheseboard = NULL;
}
if (pPieceFactory != NULL)
{
delete pPieceFactory;
pPieceFactory = NULL;
}
}
外部狀況包含棋子的色彩,內部狀況包含棋子在棋盤上的地位。終究,我們省去了多個實例對象存儲棋子色彩的空間,從而到達了空間的勤儉。
在下面的代碼中,我樹立了一個CCheseboard用於表現棋盤,棋盤類中保留了放置的黑色棋子和白色棋子;這就相當於在內部保留了同享對象的內部狀況;關於棋盤對象,我們是否是又可使用享元形式呢?再設計一個棋局類停止治理棋盤上的棋子結構,用來保留內部狀況。關於這個,這裡不停止評論辯論了。
長處
享元形式可以免年夜量異常類似對象的開支。在法式設計時,有時須要生成年夜量細粒度的類實例來表現數據。假如能發明這些實例數據除幾個參數外根本都是雷同的,應用享元形式便可以年夜幅度地削減對象的數目。
應用場所
Flyweight形式的有用性很年夜水平上取決於若何應用它和在何處應用它。當以下前提知足時,我們便可以應用享元形式了。
1.一個運用法式應用了年夜量的對象;
2.完整因為應用年夜量的對象,形成很年夜的存儲開支;
3.對象的年夜多半狀況都可變成內部狀況;
4.假如刪除對象的內部狀況,那末可以用絕對較少的同享對象代替許多組對象。
擴大
之前總結了組合形式組合形式,如今回過火來看看,享元形式就比如在組合形式的基本上加上了一個工場類,停止同享掌握。是的,組合形式有的時刻會發生許多細粒度的對象,許多時刻,我們會將享元形式和組合形式停止聯合應用。
總結
應用享元形式可以免年夜量類似對象的開支,減小了空間消費;而空間的消費是由以下幾個身分決議的:
1.實例對象削減的數量;
2.對象外部狀況的數量;對象外部狀況越多,消費的空間也會越少;
3.內部狀況是盤算的照樣存儲的;因為內部狀況能夠須要存儲,假如內部狀況存儲起來,那末空間的節儉就不會太多。
同享的Flyweight越多,存儲勤儉也就越多,勤儉量跟著同享狀況的增多而增年夜。當對象應用年夜量的外部及內部狀況,而且內部狀況是盤算出來的而非存儲的時刻,勤儉量將到達最年夜。所以,可使用兩種辦法來勤儉存儲:用同享削減外部狀況的消費;用盤算時光換取對內部狀況的存儲。
同時,在完成的時刻,必定要掌握好內部狀況與同享對象的對應關系,好比我在代碼完成部門,在CCheseboard類中應用了一個map停止彼此之間的映照,這個映照在現實開辟中須要斟酌的。
好了,享元形式就總結到這裡了。願望年夜家和我分享你對設計形式的懂得。我深信:分享使我們更提高。
PS:至於騰訊那幫店員究竟是若何完成QQ游戲年夜廳的,我也不曉得,這裡也完整是猜想的,請不要以此為基准。