设计模式的学习

OO:Object-Oriented

面向对象


《Head First设计模式》

这本书是用java写的,我是写C++的,用C++来写相关的代码


p2(第二页)

cpp 复制代码
#ifndef DUCK_H
#define DUCK_H

/**
 * @brief The Duck class    鸭子类
 */
class Duck
{
public:
    Duck();
    /**
     * @brief quack 呱呱叫
     * 所有的鸭子都会呱呱叫
     * 所以在父类中实现这部分代码
     */
    void quack();
    /**
     * @brief swim  游泳
     * 所有的鸭子都会游泳
     * 所以在父类中实现这部分代码
     */
    void swim();
    /**
     * @brief display   展示鸭子的外观
     * 每种鸭子的外观特点不同,需要在子类中进行实现
     * 所以这里是虚函数
     */
    virtual void display();
};

#endif // DUCK_H
cpp 复制代码
#ifndef MALLARDDUCK_H
#define MALLARDDUCK_H

#include "duck.h"
/**
 * @brief The MallardDuck class 绿头鸭
 */
class MallardDuck : public Duck
{
public:
    MallardDuck();
    virtual void display();
};

#endif // MALLARDDUCK_H
cpp 复制代码
#ifndef MALLARDDUCK_H
#define MALLARDDUCK_H

#include "duck.h"
/**
 * @brief The MallardDuck class 绿头鸭
 */
class MallardDuck : public Duck
{
public:
    MallardDuck();
    virtual void display();
};

#endif // MALLARDDUCK_H

现在需要添加需求:一部分鸭子会飞。

如果直接在基类中添加fly(),那么意味着所有的鸭子都会飞。(所有的子类都拥有了fly() )

而橡皮鸭子是不会飞的。


当涉及"维护"时,为了复用目的而使用继承,并不是完美的。


如果子类把fly()方法覆盖掉呢?

可以,但如果再加入一种不会飞的鸭子,那么需要重新再覆盖fly()方法。

cpp 复制代码
void fly()
{
    //覆盖,什么事都不做
}

每多一种鸭子,就需要覆盖一次fly()方法。


而有些鸭子不会飞,也不会叫。


希望找到一种建立软件的方法,让我们可以用一种对既有的代码影响最小的方式来修改软件。

这样我们就可以花较少时间重做代码,而让程序去做更酷的事。


继承不能很好地解决问题,因为鸭子的行为在子类中不断地改变,并且让所有的子类都有这些行为是不恰当的。

Flyable和Quackable接口一开始似乎还不错,但继承接口无法达到代码的复用。

当需要修改某个行为时,需要在定义此行为的类中修改它。


设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和不需要变化的代码混在一起。

---

努力让系统中的某部分改变不会影响其他部分。

---

设计原则:

针对接口编程,而不是针对实现编程。

---

简单的多态例子:

有一个抽象类Animal,两个具体的实现:Dog和Cat继承自Animal

针对实现编程:

cpp 复制代码
Dog d = new Dog();
d.bark();

针对接口编程:

cpp 复制代码
Animal * animal = new Dog();
animal.makeSound();

子类实例化的动作不再需要在代码中硬编码,而是在运行时才指定具体实现的对象,

进一步封装:

cpp 复制代码
a = getAnimal();
a.makeSound();

我们不关心实际的子类型是什么,我们只关心它知道如何正确地调用makeSound()。


cpp 复制代码
#ifndef FLYBEHAVIOR_H
#define FLYBEHAVIOR_H
#include <QDebug>
/**
 * @brief The FlyBehavior class 飞行行为
 */
class FlyBehavior
{
public:
    FlyBehavior()
    {

    }
    virtual void fly()
    {

    }
};

/**
 * @brief The FlyWithWings class    用翅膀飞行
 */
class FlyWithWings : public FlyBehavior
{
public:
    FlyWithWings()
    {

    }

    virtual void fly()
    {
        qDebug()<<"用翅膀飞行";
    }
};

/**
 * @brief The FlyNoWay class    不会飞行
 */
class FlyNoWay : public FlyBehavior
{
public:
    FlyNoWay()
    {

    }

    virtual void fly()
    {
        qDebug()<<"不会飞行";
    }
};

#endif // FLYBEHAVIOR_H
cpp 复制代码
#ifndef FLYBEHAVIOR_H
#define FLYBEHAVIOR_H
#include <QDebug>
/**
 * @brief The FlyBehavior class 飞行行为
 */
class FlyBehavior
{
public:
    FlyBehavior()
    {

    }
    virtual void fly()
    {

    }
};

/**
 * @brief The FlyWithWings class    用翅膀飞行
 */
class FlyWithWings : public FlyBehavior
{
public:
    FlyWithWings()
    {

    }

    virtual void fly()
    {
        qDebug()<<"用翅膀飞行";
    }
};

/**
 * @brief The FlyNoWay class    不会飞行
 */
class FlyNoWay : public FlyBehavior
{
public:
    FlyNoWay()
    {

    }

    virtual void fly()
    {
        qDebug()<<"不会飞行";
    }
};

#endif // FLYBEHAVIOR_H

关键在于:

鸭子现在会将飞行和呱呱叫的动作"委托"别人处理,而不是使用定义在Duck类或子类内的呱呱叫和飞行方法。

cpp 复制代码
#ifndef DUCK_H
#define DUCK_H

#include "flybehavior.h"
#include "quackbehavior.h"
/**
 * @brief The Duck class    鸭子类
 */
class Duck
{
public:
    Duck();
    FlyBehavior * flyBehavior;
    QuackBehavior * quackBehavior;
    void performQuack()
    {
        quackBehavior->quack();
    }
    void performFly()
    {
        flyBehavior->fly();
    }
    /**
     * @brief display   展示鸭子的外观
     * 每种鸭子的外观特点不同,需要在子类中进行实现
     * 所以这里是虚函数
     */
    virtual void display()
    {

    }
};

#endif // DUCK_H
cpp 复制代码
#ifndef MALLARDDUCK_H
#define MALLARDDUCK_H

#include "duck.h"
/**
 * @brief The MallardDuck class 绿头鸭
 */
class MallardDuck : public Duck
{
public:
    MallardDuck();
    virtual void display()
    {

    }
};

#endif // MALLARDDUCK_H
cpp 复制代码
#include "mallardduck.h"

MallardDuck::MallardDuck()
{
    flyBehavior = new FlyWithWings();
    quackBehavior = new MuteQuack();
}
cpp 复制代码
    MallardDuck mallardDuck;
    mallardDuck.performFly();
    mallardDuck.performQuack();

通过基类的指针析构子类时,会出现内存泄漏吗?

如果不希望内存泄漏,析构函数必须定义为虚函数。

---

设计原则:

多用组合,少用继承。

使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以"在运行时动态地改变行为"。

策略模式:

定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

上面的鸭子示例中:可以在运行时动态地切换鸭子相关的行为(算法)(用翅膀飞,不会飞)


设计是一门艺术,总是有许多可取舍的地方。

设计大师关心的是建立弹性的设计,可以维护,可以应付变化。

相关推荐
 M͏⁠͏r.D1 分钟前
UE4 材质学习笔记06(布料着色器/体积冰着色器)
学习·ue4·材质
wang_book5 分钟前
uniapp学习(003-3 vue3学习 Part.3)
前端·学习·微信小程序·小程序·uni-app·node.js·vue
佑我中华9 分钟前
HAL+M4学习记录_3
c语言·stm32·单片机·嵌入式硬件·mcu·学习
wclass-zhengge24 分钟前
JVM篇(学习预热 - 云原生时代的JVM(GraalVM))(持续更新迭代)
jvm·学习
s_little_monster1 小时前
【QT】信号和槽
开发语言·数据库·c++·经验分享·笔记·qt·学习
多喝热水-多读书1 小时前
Qt C++设计模式->中介者模式
c++·qt·设计模式·中介者模式
sealaugh321 小时前
aws(学习笔记第二课) AWS SDK(node js)
笔记·学习·aws
叫我龙翔2 小时前
【计网】【计网】从零开始学习http协议 ---理解http重定向和请求方法
服务器·网络·网络协议·学习·http
人工智能技术咨询.2 小时前
人工智能机器视觉就业前景怎么样?
人工智能·深度学习·学习·计算机视觉·语言模型
bylander2 小时前
【AI学习】Mamba学习(二):线性注意力
人工智能·学习·transformer