设计模式学习——装饰器模式

这篇笔记介绍了装饰器模式,通过 C++ 示例展示了如何动态地扩展对象的行为。文章详细讲解了基类和装饰器类的实现,并通过游戏角色的示例展示了装饰器模式的应用。

引入

在进行网络通信的时候,数据是基于IOS七层或四层网络模型(某些层合并之后就是四层模型)进行传输,通过下图可得知从应用层到物理层,数据每向下走一层就会被封装一层,最后将封装好的数据以比特流的方式发送给接收端。封装之后数据只是变得更复杂了, 并没有改变它是数据的本质。

image

在设计模式中,我们可以使用“装饰器模式”来实现这种封装。在这个过程中,我们会对原有基础类对象的行为进行扩展,但不实际改变类对象的本质。

蔚蓝

在2D平台跳跃游戏“蔚蓝”中,我们将操作角色Madeline征服_Celeste山_。游戏过程中,Madeline有时会获得一些额外的能力,如二段跳等等。Madeline仍然是Madeline,只是其能力得到了扩展。因此,我们可以使用装饰器模式来实现这一种类。

首先,为了体现出装饰器模式的强大之处,我们定义一个玩家基类Player,并让多种角色类继承自这一基类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Player {
public:
    virtual ~Player() = default;
    virtual void dash();
    virtual void groundCharge();
    const std::string & getName();
    void setDashes(int);
protected:
    std::string _name;
    int _maxdashes = 1;
    int _dashes = _maxdashes;
};
void Player::dash() {
    if(_dashes > 0) {
        std::cout << _name << " Dashes!" <<std::endl;
        _dashes--;
    }
    else {
        std::cout << _name << " Can't Dash!" << std::endl;
    }
}
void Player::groundCharge() {
    std::cout << _name << " hits ground and charged!" << std::endl;
    _maxdashes++;
}
const std::string & Player::getName() {
    return _name;
}
void Player::setDashes(int n) {
    _maxdashes = n;
    _dashes = _maxdashes;
}

在Player类中,我们实现了几种所有角色共有的技能方法:冲刺dash(),撞地充能groundCharge()。我们还定义了一些getter和setter。

接下来,我们定义MadelineEevee两个角色类——伊布也来爬_Celeste山_了!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Madeline :public Player{
public:
    Madeline();
};
Madeline::Madeline() : Player() {
    _name = "Madeline";
}
class Eevee : public Player {
public:
    Eevee();
};
Eevee::Eevee() : Player(){
    _name = "Eevee";
}

我们已经可以使用这两个类进行一些输出:

1
2
3
4
5
Madeline madeline;
    madeline.dash();
    Eevee eevee;
    eevee.dash();
    eevee.dash();
1
2
3
Madeline Dashes!
Eevee Dashes!
Eevee Can't Dash!

装饰器

我们定义装饰器类PlayerDecorator继承自Player,并重写Player的基本方法,方便后续使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class PlayerDecorator : public Player {
public:
    explicit PlayerDecorator(const Player& decorated);
    void dash() override;
    void groundCharge() override;
protected:
    Player _decorated;
};
PlayerDecorator::PlayerDecorator(const Player& decorated) : _decorated(decorated){}
void PlayerDecorator::dash() {
    _decorated.dash();
}
void PlayerDecorator::groundCharge() {
    _decorated.groundCharge();
}

可以发现,我们实际上是把需要扩展能力的角色对象放在了私有变量中,这样可以保证原有对象不变的同时实现类功能的扩展。

接下来,我们定义特定的装饰器。我们有Badeline装饰器类,用来扩展二段跳能力;我们还有Core装饰器类,表示进入_Celeste山_核心,落地不冲能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Badeline : public PlayerDecorator {
public:
    explicit Badeline(const Player& decorated);
    void dash() override;
    void groundCharge() override;
};
Badeline::Badeline(const Player& decorated) : PlayerDecorator(decorated){
    _name = _decorated.getName() + "with Badeline";
    if(_decorated.getName() == "Eevee") _decorated.setDashes(1);
    else _decorated.setDashes(2);
}
void Badeline::dash() {
    PlayerDecorator::dash();
}
void Badeline::groundCharge() {
    if(_decorated.getName() == "Eevee") {
        PlayerDecorator::groundCharge();
        std::cout << "Badeline won't help eevee, she's worst." << std::endl;
    }
    else {
        PlayerDecorator::groundCharge();
        std::cout << "With Badeline's help, " << _decorated.getName() << "get 2 Dashes!" << std::endl;
    }
}

我们可以根据不同角色对象来扩展操作。比如,Badeline不会帮助Eevee实现二段跳。她最坏了。Core类实现相似,在此省略。

接下来,我们在主函数中使用装饰器来对Madeline和Eevee进行能力的扩展:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "Madeline.h"
#include "Eevee.h"
#include "Badeline.h"
#include "Core.h"

int main() {
    Madeline madeline;
    madeline.dash();
    Eevee eevee;
    eevee.dash();
    eevee.dash();

    Badeline madeline1(madeline);
    madeline1.dash();
    madeline1.dash();
    Badeline eevee1(eevee);
    eevee1.dash();
    eevee1.dash();
    eevee1.groundCharge();

    Core madeline2(madeline);
    madeline2.dash();
    madeline2.dash();
    madeline2.groundCharge();
    madeline2.dash();

    return 0;
}

输出结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Madeline Dashes!
Eevee Dashes!
Eevee Can't Dash!
Madeline Dashes!
Madeline Dashes!
Eevee Dashes!
Eevee Can't Dash!
Eevee hits ground and charged!
Badeline won't help eevee, she's worst.
Madeline Dashes!
Madeline Dashes!
In Celeste Core, you can't charge by hitting ground!
Madeline Can't Dash!

总结

装饰器模式通过将对象包装在装饰器类中,以便动态地修改其行为。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。