欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++設(shè)計(jì)模式之享元模式

 更新時(shí)間:2014年10月08日 10:22:55   投稿:junjie  
這篇文章主要介紹了C++設(shè)計(jì)模式之享元模式,本文講解了什么是享元模式、享元模式代碼實(shí)例、享元模式的優(yōu)點(diǎn)等內(nèi)容,需要的朋友可以參考下

前言

無(wú)聊的時(shí)候,也去QQ游戲大廳玩五子棋或者象棋;作為程序員,看到一個(gè)產(chǎn)品,總要去想想它是怎么設(shè)計(jì)的,怎么完成的,我想這個(gè)是所有程序員都會(huì)做的事情吧(強(qiáng)迫癥???)。有的時(shí)候,想完了,還要做一個(gè)DEMO出來(lái),才能體現(xiàn)自己的NB,然后還有點(diǎn)小成就感。

在玩五子棋或象棋的時(shí)候,我就想過,騰訊那幫伙計(jì)是怎么做的呢?五子棋的棋子有黑白兩色,難道每次放一個(gè)棋子就new一個(gè)對(duì)象么?象棋有車、馬、相、士、帥、炮和兵,是不是每盤棋都要把所有的棋子都new出來(lái)呢?如果真的是每一個(gè)棋子都new一個(gè),那么再加上那么多人玩;那要new多少對(duì)象啊,如果是這樣做的話,我想有多少服務(wù)器都是搞不定的,可能QQ游戲大廳會(huì)比12306還糟糕。那騰訊那幫伙計(jì)是如何實(shí)現(xiàn)的呢?那就要說到今天總結(jié)的享元模式了。

什么是享元模式?

在GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書中對(duì)享元模式是這樣說的:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對(duì)象。

就如上面說的棋子,如果每個(gè)棋子都new一個(gè)對(duì)象,就會(huì)存在大量細(xì)粒度的棋子對(duì)象,這對(duì)服務(wù)器的內(nèi)存空間是一種考驗(yàn),也是一種浪費(fèi)。我們都知道,比如我在2013號(hào)房間和別人下五子棋,2014號(hào)房間也有人在下五子棋,并不會(huì)因?yàn)槲以?013號(hào)房間,而別人在2014號(hào)房間,而導(dǎo)致我們的棋子是不一樣的。這就是說,2013號(hào)房間和2014號(hào)房間的棋子都是一樣的,所有的五子棋房間的棋子都是一樣的。唯一的不同是每個(gè)棋子在不同的房間的不同棋盤的不同位置上。所以,對(duì)于棋子來(lái)說,我們不用放一個(gè)棋子就new一個(gè)棋子對(duì)象,只需要在需要的時(shí)候,去請(qǐng)求獲得對(duì)應(yīng)的棋子對(duì)象,如果沒有,就new一個(gè)棋子對(duì)象;如果有了,就直接返回棋子對(duì)象。這里以五子棋為例子,進(jìn)行分析,當(dāng)玩家在棋盤上放入第一個(gè)白色棋子時(shí),此時(shí)由于沒有白色棋子,所以就new一個(gè)白色棋子;當(dāng)另一個(gè)玩家放入第一個(gè)黑色棋子時(shí),此時(shí)由于沒有黑色棋子,所以就需要new一個(gè)黑色棋子;當(dāng)玩家再次放入一個(gè)白色棋子時(shí),就去查詢是否有已經(jīng)存在的白色棋子對(duì)象,由于第一次已經(jīng)new了一個(gè)白色棋子對(duì)象,所以,現(xiàn)在不會(huì)再次new一個(gè)白色棋子對(duì)象,而是返回以前new的白色棋子對(duì)象;對(duì)于黑色棋子,亦是同理;獲得了棋子對(duì)象,我們只需要設(shè)置棋子的不同棋盤位置即可。

UML類圖

Flyweight:描述一個(gè)接口,通過這個(gè)接口flyweight可以接受并作用于外部狀態(tài);

ConcreteFlyweight:實(shí)現(xiàn)Flyweight接口,并為定義了一些內(nèi)部狀態(tài),ConcreteFlyweight對(duì)象必須是可共享的;同時(shí),它所存儲(chǔ)的狀態(tài)必須是內(nèi)部的;即,它必須獨(dú)立于ConcreteFlyweight對(duì)象的場(chǎng)景;

UnsharedConcreteFlyweight:并非所有的Flyweight子類都需要被共享。Flyweight接口使共享成為可能,但它并不強(qiáng)制共享。

FlyweightFactory:創(chuàng)建并管理flyweight對(duì)象。它需要確保合理地共享flyweight;當(dāng)用戶請(qǐng)求一個(gè)flyweight時(shí),F(xiàn)lyweightFactory對(duì)象提供一個(gè)已創(chuàng)建的實(shí)例,如果請(qǐng)求的實(shí)例不存在的情況下,就新創(chuàng)建一個(gè)實(shí)例;

Client:維持一個(gè)對(duì)flyweight的引用;同時(shí),它需要計(jì)算或存儲(chǔ)flyweight的外部狀態(tài)。

實(shí)現(xiàn)要點(diǎn)

根據(jù)我們的經(jīng)驗(yàn),當(dāng)要將一個(gè)對(duì)象進(jìn)行共享時(shí),就需要考慮到對(duì)象的狀態(tài)問題了;不同的客戶端獲得共享的對(duì)象之后,可能會(huì)修改共享對(duì)象的某些狀態(tài);大家都修改了共享對(duì)象的狀態(tài),那么就會(huì)出現(xiàn)對(duì)象狀態(tài)的紊亂。對(duì)于享元模式,在實(shí)現(xiàn)時(shí)一定要考慮到共享對(duì)象的狀態(tài)問題。那么享元模式是如何實(shí)現(xiàn)的呢?

在享元模式中,有兩個(gè)非常重要的概念:內(nèi)部狀態(tài)和外部狀態(tài)。

內(nèi)部狀態(tài)存儲(chǔ)于flyweight中,它包含了獨(dú)立于flyweight場(chǎng)景的信息,這些信息使得flyweight可以被共享。而外部狀態(tài)取決于flyweight場(chǎng)景,并根據(jù)場(chǎng)景而變化,因此不可共享。用戶對(duì)象負(fù)責(zé)在必要的時(shí)候?qū)⑼獠繝顟B(tài)傳遞給flyweight。

flyweight執(zhí)行時(shí)所需的狀態(tài)必定是內(nèi)部的或外部的。內(nèi)部狀態(tài)存儲(chǔ)于ConcreteFlyweight對(duì)象之中;而外部對(duì)象則由Client對(duì)象存儲(chǔ)或計(jì)算。當(dāng)用戶調(diào)用flyweight對(duì)象的操作時(shí),將該狀態(tài)傳遞給它。同時(shí),用戶不應(yīng)該直接對(duì)ConcreteFlyweight類進(jìn)行實(shí)例化,而只能從FlyweightFactory對(duì)象得到ConcreteFlyweight對(duì)象,這可以保證對(duì)它們適當(dāng)?shù)剡M(jìn)行共享;由于共享一個(gè)實(shí)例,所以在創(chuàng)建這個(gè)實(shí)例時(shí),就可以考慮使用單例模式來(lái)進(jìn)行實(shí)現(xiàn)。

享元模式的工廠類維護(hù)了一個(gè)實(shí)例列表,這個(gè)列表中保存了所有的共享實(shí)例;當(dāng)用戶從享元模式的工廠類請(qǐng)求共享對(duì)象時(shí),首先查詢這個(gè)實(shí)例表,如果不存在對(duì)應(yīng)實(shí)例,則創(chuàng)建一個(gè);如果存在,則直接返回對(duì)應(yīng)的實(shí)例。

代碼實(shí)現(xiàn):

復(fù)制代碼 代碼如下:

#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;
    }
}

內(nèi)部狀態(tài)包括棋子的顏色,外部狀態(tài)包括棋子在棋盤上的位置。最終,我們省去了多個(gè)實(shí)例對(duì)象存儲(chǔ)棋子顏色的空間,從而達(dá)到了空間的節(jié)約。

在上面的代碼中,我建立了一個(gè)CCheseboard用于表示棋盤,棋盤類中保存了放置的黑色棋子和白色棋子;這就相當(dāng)于在外部保存了共享對(duì)象的外部狀態(tài);對(duì)于棋盤對(duì)象,我們是不是又可以使用享元模式呢?再設(shè)計(jì)一個(gè)棋局類進(jìn)行管理棋盤上的棋子布局,用來(lái)保存外部狀態(tài)。對(duì)于這個(gè),這里不進(jìn)行討論了。

優(yōu)點(diǎn)

享元模式可以避免大量非常相似對(duì)象的開銷。在程序設(shè)計(jì)時(shí),有時(shí)需要生成大量細(xì)粒度的類實(shí)例來(lái)表示數(shù)據(jù)。如果能發(fā)現(xiàn)這些實(shí)例數(shù)據(jù)除了幾個(gè)參數(shù)外基本都是相同的,使用享元模式就可以大幅度地減少對(duì)象的數(shù)量。

使用場(chǎng)合

Flyweight模式的有效性很大程度上取決于如何使用它以及在何處使用它。當(dāng)以下條件滿足時(shí),我們就可以使用享元模式了。

1.一個(gè)應(yīng)用程序使用了大量的對(duì)象;
2.完全由于使用大量的對(duì)象,造成很大的存儲(chǔ)開銷;
3.對(duì)象的大多數(shù)狀態(tài)都可變?yōu)橥獠繝顟B(tài);
4.如果刪除對(duì)象的外部狀態(tài),那么可以用相對(duì)較少的共享對(duì)象取代很多組對(duì)象。

擴(kuò)展

之前總結(jié)了組合模式組合模式,現(xiàn)在回過頭來(lái)看看,享元模式就好比在組合模式的基礎(chǔ)上加上了一個(gè)工廠類,進(jìn)行共享控制。是的,組合模式有的時(shí)候會(huì)產(chǎn)生很多細(xì)粒度的對(duì)象,很多時(shí)候,我們會(huì)將享元模式和組合模式進(jìn)行結(jié)合使用。

總結(jié)

使用享元模式可以避免大量相似對(duì)象的開銷,減小了空間消耗;而空間的消耗是由以下幾個(gè)因素決定的:

1.實(shí)例對(duì)象減少的數(shù)目;
2.對(duì)象內(nèi)部狀態(tài)的數(shù)目;對(duì)象內(nèi)部狀態(tài)越多,消耗的空間也會(huì)越少;
3.外部狀態(tài)是計(jì)算的還是存儲(chǔ)的;由于外部狀態(tài)可能需要存儲(chǔ),如果外部狀態(tài)存儲(chǔ)起來(lái),那么空間的節(jié)省就不會(huì)太多。

共享的Flyweight越多,存儲(chǔ)節(jié)約也就越多,節(jié)約量隨著共享狀態(tài)的增多而增大。當(dāng)對(duì)象使用大量的內(nèi)部及外部狀態(tài),并且外部狀態(tài)是計(jì)算出來(lái)的而非存儲(chǔ)的時(shí)候,節(jié)約量將達(dá)到最大。所以,可以使用兩種方法來(lái)節(jié)約存儲(chǔ):用共享減少內(nèi)部狀態(tài)的消耗;用計(jì)算時(shí)間換取對(duì)外部狀態(tài)的存儲(chǔ)。

同時(shí),在實(shí)現(xiàn)的時(shí)候,一定要控制好外部狀態(tài)與共享對(duì)象的對(duì)應(yīng)關(guān)系,比如我在代碼實(shí)現(xiàn)部分,在CCheseboard類中使用了一個(gè)map進(jìn)行彼此之間的映射,這個(gè)映射在實(shí)際開發(fā)中需要考慮的。

好了,享元模式就總結(jié)到這里了。希望大家和我分享你對(duì)設(shè)計(jì)模式的理解。我堅(jiān)信:分享使我們更進(jìn)步。
PS:至于騰訊那幫伙計(jì)到底是如何實(shí)現(xiàn)QQ游戲大廳的,我也不知道,這里也完全是猜測(cè)的,請(qǐng)不要以此為基準(zhǔn)。

相關(guān)文章

最新評(píng)論