子类沙盒

“使用基类提供的操作集合来定义子类中的行为”

一个基类定义了一个抽象的沙盒方法和一些预定义的操作集合。通过将它们设置为受保护的状态以确保它们仅 供子类使用。每个派生出的沙盒子类根据父类提供的操作来实现沙盒函数。

Superpower基类

class Superpower 
{
public:  
 virtual ~Superpower() {} 

protected:
 virtual void activate() = 0;  

 void move(double x, double y, double z)  
 {   
 // Code here...  

 } 

 void playSound(SoundId sound)  
 {   
  // Code here... 

 }  

 void spawnParticles(ParticleType type, int count) 
 {   
  // Code here...  

 } 
};

activate() 就是沙盒函数,由于它是抽象虚函数,因此子类必须要重写它。

其他的受保护函数 move、playSound、spawnParticles 都是所提供的操作, 这就是子类在 activate 函数实现时能够调用的函数。

创造一些放射性蜘蛛并创建一个power类

class SkyLaunch : public Superpower 
{ 
protected:  
 virtual void activate()  
 {   
   move(0, 0, 20);    // Spring into the air.

  
   playSound(SOUND_SPROING);   
   spawnParticles(PARTICLE_DUST, 10);   
 } 
}; 

继续优化,将获取英雄位置方法提到Superpower基类

class Superpower
{
protected:
 double getHeroX() { /* Code here... 

*/ }
 double getHeroY() { /* Code here...

 */ }
 double getHeroZ() { /* Code here...

 */ }

 // Existing stuff...

};

在SkyLaunch子类中使用它们

class SkyLaunch : public Superpower 
{ 
protected:  
 virtual void activate() 
 {   
  if (getHeroZ() == 0)  
  {   
    // On the ground, so spring into the air.

  
    playSound(SOUND_SPROING);
    spawnParticles(PARTICLE_DUST, 10);  
    move(0, 0, 20);   
  }   
  else if (getHeroZ() < 10.0f)  
  {    
    // Near the ground, so do a double jump.

  
    playSound(SOUND_SWOOP);   
    move(0, 0, getHeroZ() - 20);  
  }  
  else  
  {   
     // Way up in the air, so do a dive attack.  

 
     playSound(SOUND_DIVE);   
     spawnParticles(PARTICLE_SPARKLES, 1);  
     move(0, 0, -getHeroZ());  
  } 
 } 
}; 

像Superpower基类中playSound可能有,不想让子类接触的状态,比如

void Superpower::playSound(SoundId sound)
{
    soundEngine_.play(sound);
}

直接提供函数,还是由包含它们的对象提供

例如可能,基类塞的方法越来越臃肿

class Superpower
{
protected:
  void playSound(SoundId sound) { /* Code...
*/ }
  void stopSound(SoundId sound) { /* Code... 

*/ }
  void setVolume(SoundId sound) { /* Code...

 */ }

  // Sandbox method and other operations...

};

可以将基类内容封装出来,用组合的方式

class SoundPlayer
{
 void playSound(SoundId sound) { /* Code... 

*/ }
 void stopSound(SoundId sound) { /* Code... 

*/ }
 void setVolume(SoundId sound) { /* Code...

 */ }
};

在Superpower中使用SoundPlayer

class Superpower 
{ 
protected:  
 SoundPlayer& getSoundPlayer()  
 {   
   return soundPlayer_;  
 }  

 // Sandbox method and other operations... 

private:
 SoundPlayer soundPlayer_; 
}; 

子类中通过getSoundPlayer方法返回的SoundPlayer对象执行相应方法。

具体怎么做这需要具体应用情况下自己思考。

基类如何获取其所需的状态

把它传递给基类构造函数

class Superpower 
{ 
public:  
 Superpower(ParticleSystem* particles)  
 : particles_(particles) {}  
 // Sandbox method and other operations... 


private:  
 ParticleSystem* particles_;   
};

class SkyLaunch : public Superpower 
{ 
public:  
 SkyLaunch(ParticleSystem* particles)  
 : Superpower(particles)  {} 
};

这样不得不在每个子类构造函数都做处理。

进行分段初始化

Superpower* power = new SkyLaunch(); 
power->init(particles);

Superpower* createSkyLaunch(
  ParticleSystem* particles)
{  
 Superpower* power = new SkyLaunch();  
 power->init(particles);  
 return power; 
}

状态静态化

如果所有实例都共用同一个,则可以考虑,将状态静态化,尽早地把init调用给初始化号。

class Superpower 
{ 
public:  
 static void init(ParticleSystem* particles)  
 {   
   particles_ = particles;  
 }  

 // Sandbox method and other operations... 

 
private:  
 static ParticleSystem* particles_; 
};

使用服务定位器

从某处,用的时候去获取

class Superpower 
{ 
protected:  
 void spawnParticles(ParticleType type, int count)  
 {   
  ParticleSystem& particles = Locator::getParticles();   
  particles.spawn(type, count);  
 }  

 // Sandbox method and other operations... 

}; 

这种方式大多数情况是体验比较好的。