出于对自己的屎山代码的难以忍受,终于在2023.2开始了《游戏编程模式》的学习,总结了一些书里面提到的模式。有些很少用就暂时没去总结。本书共有19种模式,部分模式是游戏编程常用而其他行业软件开发不怎么用的。
单例模式
优缺点:
1)某些管理类需要满足,全局只有一个实例,如文件读入读出系统,如果有两个实例,
会导致,同时对同一个文件进行写入,造成冲突
2)这些管理类,按照传统的设计模式,应该提供全局访问点。
3)当希望保证只有一个实例,无需提供全局访问点的方式:
/*---------------------------------单例模式-----------------------------
*/
// -------------------------------Demo-----------------------------//
// 准确的说,这是一个静态类
class FileSystem
{
private:
FileSystem();
static FileSystem _instance;
public:
static FileSystem &instance() { return _instance; }
// 这个才是真正意义的单例
/*
static FileSystem &instance()
{
FileSystem *instance = new FileSystem();
return *instance;
}
*/
};
服务定位器
更加灵活、更加可配置的单例模式扩展。
/*---------------------------------服务定位器模式-----------------------------
*/
// -------------------------------Demo-----------------------------//
// 服务
class Audio
{
public:
virtual void playSound(int ID) = 0;
virtual void stopSound(int ID) = 0;
virtual ~Audio() {}
};
// 实现类
class ConsoleAudio : public Audio
{
public:
virtual void playSound(int ID) override
{
// 实现播放声音
}
virtual void stopSound(int ID) override
{
// 实现停止播放
}
};
// 服务定位器
class Locator
{
private:
static Audio *_service;
static NullAudio _nullAudio;
public:
static void initialize() { _service = &_nullAudio; }
// 暴露的服务接口,调用者只需要调用getAudio()就可以获得服务,无需知道谁提供了服务
static Audio &getAudio() { return *_service; }
// 依赖注入,外部代码负责注入定位器需要的服务
static void provide(Audio *service)
{
if (service == nullptr)
initialize();
else
_service = service;
}
};
// 为了保证Locator::getAudio()返回的不是一个空指针,使用一个空对象
class NullAudio : public Audio
{
public:
virtual void playSound(int ID) override
{ /* 什么也不做 */
}
virtual void stopSound(int ID) override
{ /* 什么也不做 */
}
};
// main function
int main(int argc, char const *argv[])
{
// 创建真正提供播放声音服务的类对象
ConsoleAudio *audio = new ConsoleAudio();
// 注入定位器
Locator::provide(audio);
// 获得服务
Locator::getAudio().playSound(0);
return 0;
}
扩展:装饰器模式
可以用来实现日志
/* -------------------------------装饰器模式------------------------------
*/
// -------------------------------Demo----------------------------------
class LoggedAudio : public Audio
{
private:
void log(const char *message)
{
// 记录日志的代码
}
// 被装饰的对象
Audio &_wrapped;
public:
void enableAudioLogging()
{
// 装饰现有的服务
Audio *service = new LoggedAudio(Locator::getAudio());
// 注入新服务
Locator::provide(service);
}
LoggedAudio(Audio &wrapped) : _wrapped(wrapped) {}
// 转发相应的命令
virtual void playSound(int ID) override
{
log("Play sound");
_wrapped.playSound(ID);
}
virtual void stopSound(int ID) override
{
log("Stop sound");
_wrapped.stopSound(ID);
}
};