站务联系

内容

游戏设计模式Decoupling Patterns(2)

发布时间:2021-03-17   来源:网络整理    
字号:

我有时据说这被称为“时序耦合”——两块分离的代码应当以正确的次序读取,才能使程序正确运行。有状态的硬件某些程度上都有此类状况,但是如同其他耦合一样,减少时序耦合使代码库更容易管理。

幸运的是,还有一种设计方式称作“空对象”,我们可用它处理这个。基本思路是在我们没能找到服务或则程序没以正确的次序读取时,不返回NULL,而是返回一个特定的,实现了恳求对象一样插口的对象。它的实现哪些也不做,但是它保证读取服务的代码能获取至对象,保证代码如同收到了“真的”服务对象一样安全运行。

为了使用它,我们定义另一个“空”服务提供者:

class NullAudio: public Audio
{
public:
  virtual void playSound(int soundID) { /* 什么也不做 */ }
  virtual void stopSound(int soundID) { /* 什么也不做 */ }
  virtual void stopAllSounds()        { /* 什么也不做 */ }
};

游戏设计模式Decoupling Patternsstatic void initialize() { service_ = &nullService_; } static Audio& getAudio() { return *service_; } static void provide(Audio* service) { if (service == NULL) { // 退回空服务 service_ = &nullService_; } else { service_ = service; } } private: static Audio* service_; static NullAudio nullService_; };

你虽然注意到我们用引用而非表针返回服务。由于C++中的引用(理论上)永远不是NULL,返回引用是提示用户:总可以期盼荣获一个合法的对象。

另一件值得注意的事是我们在provide()而不是访问者中检测NULL。那还要我们早早读取initialize(),保证定位器可以正确找到默认的空服务提供者。作为收益,它将分支移出了getAudio(),这在每天使用服务时节省了检测费用。

调用代码永远不知道“真正的”服务没找到,也毋须怀疑处理NULL。这保证了它永远能荣获有效的对象。

这对蓄意找不到服务也很有用。如果我们想暂时停用系统,现在有更简略的形式来实现这点了:很简单,不要在定位器中注册服务,定位器会默认使用空服务提供器。

在开发中能关掉音频是太便利的。它释放了一些显存跟CPU循环。更重要的是,当你使用debugger时刚好爆发怪声,它能避免你的声带爆裂。没有哪些东西比二十毫秒的最高音量惊叫循环更能使你尿液逆流的了。

现在我们的系统十分强壮了,让我们讨论这个机制准许的另一个弊端——装饰服务。我会例子说明。

在开发过程中,记录有趣事情发生的小小日志系统可助你查出游戏引擎正处于什么状态。如果你在处理AI,你要知道那个实体改变了AI状态。如果你是音频程序员,你或许想记录每位播放的声音,这样你可以检测他们是否是以正确的次序触发。

通常的解决方案是向代码中扔些对log()函数的读取。不幸的是,这是用一个问题代替了另一个——现在我们有很多日志了。AI程序员不关心声音在什么时候播放,声音程序员也不在意AI状态转化,但是目前都得在对方的日志中奔波。

图说天下

×
二维码生成