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();
通过基类的指针析构子类时,会出现内存泄漏吗?
如果不希望内存泄漏,析构函数必须定义为虚函数。
---
设计原则:
多用组合,少用继承。
使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以"在运行时动态地改变行为"。
策略模式:
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
上面的鸭子示例中:可以在运行时动态地切换鸭子相关的行为(算法)(用翅膀飞,不会飞)
设计是一门艺术,总是有许多可取舍的地方。
设计大师关心的是建立弹性的设计,可以维护,可以应付变化。