【设计模式】之六大设计原则--上篇

简介

这个专栏是阅读”设计模式之禅”第二版做的阅读笔记。

将书中部分的例子从java改成c++

单一职责原则(Single Responsibility Principle)

define: There should never be more than one reason for a class to change.

单一职责原则的定义是:应该有且仅有一个原因引起类的变更。

分歧点:如何对一个类进行职责划分

advice:接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化

书中也表明,在实际项目中很难实现单一职责,但是这不能否定这个原则的优点。

里氏替换原则(Liskov Substitution Principle)

define: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

(如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。)

通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

含义:

1 子类必须完全实现父类的方法

ps:

在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。

eg.

玩具枪不能继承Cs抽象枪的类,因为玩具枪不具备击杀敌人的效果。

2 子类可以有自己的个性

子类出现的地方父类未必可以出现,因为子类有可能带有自己的个性。

3 覆盖或实现父类的方法时输入参数可以被放大

4 覆写或实现父类的方法时初出结果可以被缩小

书中的例子:

#include <iostream>

using namespace std;

class AbstractGun {
public:
    virtual void shoot() {}
};

class Soldier {
public:
    void setGun(AbstractGun& gun) {
        mgun = &gun;
    }
    void killEnemy() {
        cout << "士兵开始击杀敌人..." << endl;
        mgun->shoot();
    }
private:
    AbstractGun *mgun;
};

class Handgun : public AbstractGun {
public:
    virtual void shoot() {
        cout << "手枪射击..." << endl;
    }
};

class Rifle : public AbstractGun {
public:
    virtual void shoot() {
        cout << "步枪射击..." << endl;
    }
};


class Machinegun : public AbstractGun {
public:
    virtual void shoot() {
        cout << "机枪扫射..." << endl;
    }
};

class AK47 : public Rifle {
public:
    virtual void shoot() {
        cout << "AK47射击..." << endl;
    }
};

class AUG : public Rifle {
public:
    void zoomOut() {
        cout << "通过望远镜查看敌人..." << endl;
    }
    void shoot() {
        cout << "AUG射击..." << endl;
    }
};

class Snipper {
public:
    void killEnemy(AUG& aug) {
        aug.zoomOut();
        aug.shoot();
    }
};

void test1() {
    Soldier zl;
    Rifle gun;
    zl.setGun(gun);
    zl.killEnemy();
}

void test2() {
    Snipper san;
    AUG aug;
    san.killEnemy(aug);
}

int main() {
    return 0;
}

依赖倒置原则(Dependence Inversion Principle)

define: High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

高层模块不应该依赖底层模块,两个都应该依赖抽象

抽象不依赖实现,实现依赖抽象

面向对象设计 OOD(Object-Oriented Design)

模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的

接口或抽象类不依赖于实现类

实现类依赖接口或抽象类

减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码可读性和可维护性

三种写法

1 构造函数传递依赖对象

class IDriver {
public:
    virtual void drive() {}
};

class Driver : public IDriver {
public:
    Driver(ICar* car) {
        mcar = car;
    }
    virtual void drive() {
        mcar->run();
    }
private:
    ICar* mcar;
};

2 Setter方法传递依赖对象

class IDriver {
public:
    virtual void setCar(ICar *car) {}
    virtual void drive() {}
};
class Driver : public IDriver {
public:
    virtual void setCar(ICar *car) {
        mcar = car;
    }
    virtual void drive() {
        mcar->run();
    }
private:
    ICar* mcar;
};

3 接口声明依赖对象(也叫接口注入)

class IDriver {
public:
    virtual void drive (ICar *car) {}
};
class Driver : class IDriver {
public:
    virtual void drive (ICar *car) {
        car->run();
    }
};

依赖倒置原则的本质就是面向接口编程,利用接口抽象类实现各个类或模块间实现独立,不相互影响,实现模块间的松耦合。

在实际项目中使用规则:

1 每个类尽量都有接口或抽象类,这是依赖倒置原则的基本要求。

2 变量的表面类型尽量是接口或抽象类,表面类型是在定义的时候赋予的类型,实际类型是对象的类型。

3 任何类都不应该从具体类派生

4 尽量不要覆写基类已经实现的方法

5 结合里氏替换原则使用

书中的例子:

#include <iostream>

using namespace std;

class ICar {
public:
    virtual void run() {}
};

class Benz : public ICar {
public:
    virtual void run() {
        cout << "run Benz...." << endl;
    }
};

class BWM : public ICar {
public:
    virtual void run() {
        cout << "run BWM...." << endl;
    }
};

class IDriver {
public:
    virtual void drive (ICar *car) {}
};

class Driver : public IDriver {
public:
    virtual void drive (ICar *car) {
        car->run();
    }
};

void test0() {
    Driver zl;
    zl.drive(new Benz());
}

int main() {
    test0();
    return 0;
}

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦