天天看點

閑談原型模式——基于UI動畫架構1. 原型模式2. 架構中的應用3. 結語

  前幾篇文章介紹了CEGUI動畫架構中主要的幾個設計模式。本文接着讨論“原型模式”在動畫架構中的應用。

1. 原型模式

  原型模式的定義:

用原型執行個體指定建立對象的種類,通過拷貝這些原型建立新的對象。

  類圖:

閑談原型模式——基于UI動畫架構1. 原型模式2. 架構中的應用3. 結語

  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;
}
           

  動畫類的原型模式類圖:

閑談原型模式——基于UI動畫架構1. 原型模式2. 架構中的應用3. 結語

  代碼中可以看到,單一動畫的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; // 目前執行到哪個政策,下一幀從這個政策開始繼續執行
};
           

類圖:

閑談原型模式——基于UI動畫架構1. 原型模式2. 架構中的應用3. 結語

3. 結語

  原型模式的本質是複制對象,隻有各具體派生類自己才清楚地知道怎麼複制自己,進而産生一個副本;是以在不知道對象的具體類型時想要對象的一個副本的情況下,可以考慮原型模式。

繼續閱讀