“使用基类提供的操作集合来定义子类中的行为”
一个基类定义了一个抽象的沙盒方法和一些预定义的操作集合。通过将它们设置为受保护的状态以确保它们仅 供子类使用。每个派生出的沙盒子类根据父类提供的操作来实现沙盒函数。
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...
}; 这种方式大多数情况是体验比较好的。