最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

游戲編程模式(四):原型模式和單例模式

2023-07-25 21:24 作者:寧牁兒  | 我要投稿

原型模式

原型模式是一種對象創(chuàng)建型模式,它是使用原型實例指定待創(chuàng)建對象的類型,并且通過復(fù)制這個原型來創(chuàng)建新的對象。

它的工作原理很簡單:將一個原型對象傳給要發(fā)動創(chuàng)建的對象(即客戶端對象),這個要發(fā)動創(chuàng)建的對象通過請求原型對象復(fù)制自己來實現(xiàn)創(chuàng)建過程。

在Java和C#中,對象類中的clone()方法就是原型模式的應(yīng)用,在游戲開發(fā)中,考慮一個怪物生成的案例,不利用原型模式的代碼將會是:

class Spawner??

{??

public:??

virtual ~Spawner() {}??

virtual Monster* spawnMonster() = 0;??

};??

class GhostSpawner : public Spawner??

{??

public:??

virtual Monster* spawnMonster()??

{??

return new Ghost();??

}??

};??

class DemonSpawner : public Spawner??

{??

public:??

virtual Monster* spawnMonster()??

{??

return new Demon();??

}??

};

我們可以把clone()方法放入Monster類中,使其可以生成出一個自己的副本:

class Monster??

{??

public:??

virtual ~Monster() {}??

virtual Monster* clone() = 0;??

// ……??

};


class Ghost : public Monster {??

public:??

Ghost(int health, int speed)??

: health_(health),??

speed_(speed)??

{}??

virtual Monster* clone()??

{??

return new Ghost(health_, speed_);??

}??

private:??

int health_;??

int speed_;??

};

將擁有clone()方法的原型類送入客戶端對象(spawner類):

class Spawner??

{??

public:??

Spawner(Monster* prototype)??

: prototype_(prototype)??

{}??

Monster* spawnMonster()??

{??

return prototype_->clone();??

}??

private:??

Monster* prototype_;??

};

當(dāng)然,在實際開發(fā)中,通常需要注意clone()方法具體是做淺拷貝還是深拷貝,這個點在一般的軟件開發(fā)中都會遇到,這里打算只是簡單介紹原型模式的基本思想,就不繼續(xù)深入討論了。

單例模式

單例模式在軟件開發(fā)中出現(xiàn)率太過于頻繁,以至于我不打算集中注意力去討論如何使用它,而是討論如何避免使用它。因為盡管它確實非常方便,但在游戲開發(fā)中,更應(yīng)該謹(jǐn)慎地使用這個模式。

GoF中這樣描述單例模式:

保證一個類只有一個實例,并且提供了訪問該實例的全局訪問點。

快速過一遍它的最簡單的經(jīng)典實現(xiàn)方案(當(dāng)然此處不打算討論線程安全的實現(xiàn)方案):

class FileSystem??

{??

public:??

static FileSystem& instance()??

{??

// 惰性初始化(非線程安全)

if (instance_ == NULL) instance_ = new FileSystem();??

return *instance_;??

}??

private:??

FileSystem() {}??

static FileSystem* instance_;??

};

很明顯,用單例模式的最大好處就是該單例類在任何需要的地方都可用,而無需笨重地到處傳遞,在很多只需要一個實例的場景中,也保證了不會因不小心創(chuàng)建了多個實例而造成混亂。但是,我們需要考慮它可能帶來的各種麻煩事:

一、它是一個全局變量

  • 降低代碼的可讀性。要理解一個單例類在某個方法中干了些啥,得追蹤整個代碼庫來搜尋什么修改了全局變量

  • 增加了耦合性。全局變量很容易導(dǎo)致不小心在某處將兩塊不相干的模塊耦合起來

  • 多線程不友好。將某個變量轉(zhuǎn)化為全局變量時,就等于創(chuàng)建了一塊每個線程都能訪問的內(nèi)存,要保證這塊內(nèi)存的線程安全性就會變得很困難,競爭狀態(tài)、死鎖、線程同步出現(xiàn)故障的概率將大大提升

二、實例的數(shù)量被嚴(yán)格約束

單例模式當(dāng)然是只用來創(chuàng)建唯一實例的,比如日志類,為了避免日志類在眾多方法中傳來傳去,單例模式確實是一個很好的解決方式。但是,這也使得單例類只能有唯一的一個實例,假如我們需要將日志分類記錄,它將不再允許我們創(chuàng)建多個實例。

三、惰性初始化剝奪了控制權(quán)

在一般的軟件開發(fā)中,惰性初始化確實可以幫助節(jié)省內(nèi)存,只在我們需要它的時候才會占用內(nèi)存。但對于游戲這種對優(yōu)化要求程序非常高的應(yīng)用來說,惰性初始化可能會導(dǎo)致降低游戲體驗。例如,游玩中在達(dá)到一個高潮階段時,可能會出現(xiàn)大量的畫面渲染、音樂播放等需求,它們都可能是首次被調(diào)用,如果放任惰性初始化不管,此時就可能會有十萬百萬千萬個實例被同時初始化,這將導(dǎo)致肉眼可見的掉幀和斷續(xù)。


好,單例模式確實會帶來一些問題,所以使用它就需要在一些方面做出權(quán)衡,我經(jīng)常會在游戲源碼中見到各種“管理器”類,開發(fā)者想要用這些類去管理其它對象。比如,怪物管理器類、粒子管理器類、聲音管理器類,甚至,管理器管理器類,例:

class BulletManager??

{??

public:??

Bullet* create(int x, int y)??

{??

Bullet* bullet = new Bullet();??

bullet->setX(x);??

bullet->setY(y);??

return bullet;??

}??

bool isOnScreen(Bullet& bullet)??

{??

return bullet.getX() >= 0 &&??

bullet.getX() < SCREEN_WIDTH &&??

bullet.getY() >= 0 &&??

bullet.getY() < SCREEN_HEIGHT;??

}

...

}

像是上面這種Manager類純屬多余,這屬于開發(fā)者對OOP的不熟悉,完全可以將它的功能在Bullet類本身中實現(xiàn):

class Bullet??

{??

public:??

Bullet(int x, int y) : x_(x), y_(y) {}??

bool isOnScreen()??

{??

return x_ >= 0 && x_ < SCREEN_WIDTH &&??

y_ >= 0 && y_ < SCREEN_HEIGHT;??

}??

...??

private:??

int x_, y_;??

};

關(guān)于訪問權(quán)限的控制,考慮兩個案例,一、從基類中獲取到單例對象。二、將各個單例類合并到一個單例類中,而不必真正將它們單例化:

一、從基類中獲取到單例對象

很多游戲引擎中都會有GameObject基類,我們可以利用這點來從GameObject類中獲取單例對象:

class GameObject??

{??

protected:??

Log& getLog() { return log_; }??

private:??

static Log& log_;??

};??

class Enemy : public GameObject??

{??

void doSomething()??

{??

getLog().write("log!");??

}??

};

這保證任何GameObject之外的代碼都不能接觸Log對象,但是每個派生的實體確實能使用getLog()

二、合并到一個單例類中

創(chuàng)建一個代表整個游戲狀態(tài)的Game類,讓這個全局對象捎帶上其它類,來減少全局變量類的數(shù)量,而不必讓Log,F(xiàn)ileSystem和AudioPlayer都變成單例:

class Game??

{??

public:??

static Game& instance() { return instance_; }??

// 設(shè)置log_, et. al. ……??

Log& getLog() { return *log_; }??

FileSystem& getFileSystem() { return *fileSystem_; }??

AudioPlayer& getAudioPlayer() { return *audioPlayer_; }??

private:??

static Game instance_;??

Log *log_;??

FileSystem *fileSystem_;??

AudioPlayer *audioPlayer_;??

};

...

Game::instance().getAudioPlayer().play(VERY_LOUD_BANG);


游戲編程模式(四):原型模式和單例模式的評論 (共 條)

分享到微博請遵守國家法律
浪卡子县| 大悟县| 台北市| 蕲春县| 阳东县| 廉江市| 会同县| 唐河县| 蓝山县| 秀山| 大理市| 琼海市| 沈丘县| 浦东新区| 贺兰县| 永顺县| 抚远县| 伽师县| 慈利县| 木兰县| 延川县| 宁津县| 正蓝旗| 浮梁县| 家居| 孟州市| 昔阳县| 天峨县| 泊头市| 麻栗坡县| 启东市| 湄潭县| 黑河市| 余姚市| 德昌县| 保亭| 天祝| 常熟市| 十堰市| 红河县| 和硕县|