前幾篇文章介紹了CEGUI動畫架構中主要的幾個設計模式。本文接着讨論“原型模式”在動畫架構中的應用。
1. 原型模式
原型模式的定義:
用原型執行個體指定建立對象的種類,通過拷貝這些原型建立新的對象。
類圖:
Prototype:聲明一個可以複制自身的接口,用來限制想要複制自身的類,要求它們都要實作Clone方法。
ConcretePrototypeX:實作Prototype接口的類,這些類真正實作了複制自身的功能。
Client:使用原型的用戶端,調用原型執行個體對象(可能并不知道具體類型)的Clone方法來建立新的對象執行個體。
C++中,Prototype繼承體系中的Clone方法是個虛函數,而派生類中的Clone方法可能需要傳回自身類型的指針,正好可以利用C++的協變傳回機制。上面的類圖中ConcretePrototypeX實作的Clone接口的傳回值類型寫成了自身類型的指針也是有意為之。
2. 架構中的應用
2.1. 動畫原型模式
我們在xml檔案中配置的動畫(代碼),實際上隻是一個模闆,在程式啟動時加載這些配置到記憶體中,經過解釋器解釋之後,以抽象文法樹的形式存在;在運作時,并不是将這棵樹拿來直接使用,而是用這棵樹的一個副本來驅動視窗動畫。我們先看一下單一動畫:
class SingleAnimation : public Animation
{
private:
virtual SingleAnimation* Clone() const // 協變機制傳回
{
SingleAnimation* sa = new SingleAnimation(clipped);
sa->strategy = strategy->Clone();
return sa;
}
private:
bool clipped; // 是否被父視窗裁剪
Window* aniWnd; // 動畫視窗對象
IStrategy* strategy; // 動畫的控制政策
};
因為同一個動畫效果可能同時作用在不同的視窗上,是以需要用動畫模闆的副本。而對于客戶代碼來說,并不知道這個動畫模闆是SingleAnimation動畫還是GroupAnimation動畫,或者GroupAnimation是GroupAnimationSerial還是GroupAnimationParallel,客戶代碼隻需要目前動畫模闆的一個副本,它能做的,就是一句話:我需要你的一個副本,你幫忙複制一下你自己吧。來看一下動畫組的Clone接口:
// 串行動畫組
GroupAnimationSerial* GroupAnimationSerial::Clone() const
{
GroupAnimationSerial* gas = new GroupAnimationSerial();
for (auto animation : animations)
gas->AddAnimation(animation->Clone());
return gas;
}
// 并行動畫組
GroupAnimationParallel* GroupAnimationParallel::Clone() const
{
GroupAnimationParallel* gap = new GroupAnimationParallel();
for (auto animation : animations)
gap->AddAnimation(animation->Clone());
return gap;
}
動畫類的原型模式類圖:
代碼中可以看到,單一動畫的Clone接口中,動畫的控制政策字段也進行複制了:
是以動畫政策也使用了“原型模式”。
2.2. 政策原型模式
因為動畫政策也是一個模闆,同一個政策效果可能會同時作用在不同的視窗上,是以使用原型模式也是很自然的選擇:
class IStrategy
{
public:
// 同一個政策可能會有多個執行個體,是以需要一個Clone
virtual IStrategy* Clone() const = 0;
};
class FadeStrategy : public IStrategy
{
private:
virtual FadeStrategy* Clone() const // 協變機制傳回
{
return new FadeStrategy(iniAlpha, delta, minAlpha, maxAlpha);
}
private:
const float iniAlpha; // alpha初始值
float delta; // 幀與幀之間的alpha變化的百分比
const float minAlpha; // alpha的下限
const float maxAlpha; // alpha的上限
};
再看一下政策組的Clone接口:
class Strategys : public IStrategy
{
protected:
std::vector<IStrategy*> strategys;
int times; // 政策組施加控制的次數,預設為1次
const int ctimes; // 用于恢複
};
// 串行政策組
class StrategysSerial : public Strategys
{
private:
virtual StrategysSerial* Clone()
{
// 0初始化cur,times, ctimes分别初始化基類Strategys的對應字段
StrategysSerial* ss = new StrategysSerial(0, times, ctimes);
for (auto s : strategys)
ss->AddStrategy(s->Clone());
return ss;
}
private:
int cur; // 目前正在施加控制的政策的索引
};
// 并行政策組
class StrategysParallel : public Strategys
{
private:
virtual StrategysParallel* Clone()
{
// times, ctimes分别初始化基類Strategys的對應字段
StrategysParallel* sp = new StrategysParallel(times, ctimes);
for (auto s : strategys)
sp->AddStrategy(s->Clone());
sp->cur = sp->strategys.begin();
return sp;
}
private:
std::vector<IStrategy*>::const_iterator cur; // 目前執行到哪個政策,下一幀從這個政策開始繼續執行
};
類圖:
3. 結語
原型模式的本質是複制對象,隻有各具體派生類自己才清楚地知道怎麼複制自己,進而産生一個副本;是以在不知道對象的具體類型時想要對象的一個副本的情況下,可以考慮原型模式。