二、7大设计原则

7大原则

1.开闭原则

定义:Software entities like classes, modules and functions should be open for extension but closed for modification(一个软件实体如类,模块和函数应该对扩展开放,对修改关闭)

简单来说就是:类的改动是通过增加代码进行的,而不是修改源代码

业务员.png
#include <iostream>
using namespace std;
/*
如果想要再添加新功能,需要再次添加新的成员函数
这样的话,会使类越来越臃肿。
*/
class BankWorker
{
public:
  void save(){
     cout<<"存款"<<endl;
    }
  void transfer(){
     cout<<"转账"<<endl;
    }
  void pay(){
     cout<<"交费"<<endl;
     }
/*
如果我们要添加
申购基金、网银开通、贷款等其他业务
难道还要继续添加吗???
*/
};

int main(void)
{
  BankWorker* bw =new BankWorker;
  bw-­‐>pay();
  bw-­‐>transfer();
  bw-­‐>save();
  delete bw;
  return 0;
}

以上对银行业务员类(BankWorker)的设计就违背了开闭原则。因为要是改动BankWorker,添加新的功能,就必须要修改该类的源代码。
那么,如何使这段代码符合开闭原则呢?

#include <iostream>
using namespace std;
class AbBankWorker
{
public:
/* 纯虚函数 用来抽象 银行业务员的业务 */
  virtual void doBusiness() = 0;
};

class saveBankWorker:publicAbBankWorker
{
public:
  virtual void doBusiness(){
    cout<<"存款"<<endl;
  }
};

class transBankWorker:publicAbBankWorker
{
public:
  virtual void doBusiness(){
    cout<<"转账"<<endl;
  }
};

class payBankWorker:publicAbBankWorker
{
  virtual void doBusiness(){
   cout<<"付款"<<endl;
  }
};
/*
我们现在想开通 基金办理 业务员
那么只需要再集成基类,实现业务虚函数即可
*/
class fundBankWorker:publicAbBankWorker
{
  virtual void doBusiness(){
  cout<<"基金办理"<<endl;
  }
};
int main(void)
{
  /*
   产生多态的3个必要条件
   1.有继承,比如,saveBankWorker继承了AbBankWorker
   2.要有重写,这里AbBankWorker的doBusiness()接口被重写
   3.父类指针指向子类对象
*/

  AbBankWorker *abw=NULL;
  abw = new saveBankWorker;
  abw­‐>doBusiness();
  delete abw;
  abw = NULL;

  abw = new transBankWorker;
  abw-‐>doBusiness();
  delete abw;
  abw = NULL;

  abw = new payBankWorker;
  abw‐>doBusiness();
  delete abw;
  abw = NULL;

  abw = new fundBankWorker;
  abw‐>doBusiness();
  delete abw;
  abw = NULL;

  return 0;
}

这样,如果我们给银行业务员添加业务,那么无需修改原来的类中代码,而是通过拓展添加类的方式来搞定,实际上是利用了多态的特性,这样就符合了开闭原则。

2.依赖倒置原则

定义: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,面向对象设计)的精髓之一

2.1案例
传统设计

传统的过程式设计倾向于使高层次的模块依赖于低层次的模块,抽象层依赖于具体的层次。

image.png

如图分析,最上层是高层业务逻辑层,让张三区开奔驰,依赖于创建张三这个类,开奔驰就创建奔驰这个类,接着在张三类里面创建成员函数;当开BMW时,就又要在张三类中创建另一个成员函数。同理,当李四需要去开奔驰或宝马时,就需要重新创建李四类,添加开奔驰和宝马的方法,具体代码如下:

class Benz
{
public:
    void run() {
        cout << "奔驰启动了" << endl;
    }
};

class BMW
{
public:
    void run() {
        cout << "宝马启动了" << endl;
    }
};

class Zhang3
{
public:
    void driveBenz(Benz *b) {
        b->run();
    }

    void driveBMW(BMW *b)
    {
        b->run();
    }
};

//业务
int main(void)
{
#if 0
    //张三去开奔驰
    Benz  *benz = new Benz;
    Zhang3 *z3 = new Zhang3;
    z3->driveBenz(benz);

    //张三去开宝马
    BMW * bmw = new BMW;
    z3->driveBMW(bmw);

    //李四.....
    //....
        return 0;
}

依赖倒转后:

依赖倒置

将人driver和车car变成抽象类,利用接口耦合


//------抽象层------

class Car
{
public:
    virtual void run() = 0;
};

class Driver {
public:
    virtual void drive(Car *car) = 0;
};

void travel(Driver *d, Car *c)
{
    d->drive(c);
}



//实现层-----
class Zhang3 :public Driver{
public:
    virtual void drive(Car *car)
    {
        cout << "涨3 开车了" << endl;
        car->run();
    }
};
class Li4 :public Driver{
public:
    virtual void drive(Car *car)
    {
        cout << "Li4 开车了" << endl;
        car->run();
    }
};

class Benz :public Car
{
public:
    virtual void run() {
        cout << "benz 启动了" << endl;
    }
};
class BMW :public Car
{
public:
    virtual void run() {
        cout << "BMW 启动了" << endl;
    }
};

//业务
int main(void)
{
    //张三去开奔驰
    Car * benz = new Benz;
    Driver* zang3 = new Zhang3;

    zang3->drive(benz);

    //li4 区开宝马
    Car * bmw = new BMW;
    Driver *li4 = new Li4;

    li4->drive(bmw);

    
    return 0;
}
依赖倒置2

传统的设计模式通常是自顶向下逐级依赖,这样,底层模块,中间层模块和高层模块的耦合度极高,若任意修改其中的一个,很容易导致全面积的修改,非常麻烦,那么依赖倒转原则利用多态的先天特性,对中间抽象层进行依赖,这样,底层和高层之间进行了解耦合

2.2案例2——电脑组装
#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>

using namespace std;

//抽象层--------
class CPU
{
public:
    virtual void caculate() = 0;
};

class Card
{
public:
    virtual void display() = 0;
};

class Memory
{
public:
    virtual void storage() = 0;
};

//架构类
class Computer
{
public:
    Computer(CPU *cpu, Card*card, Memory * mem)
    {
        this->cpu = cpu;
        this->card = card;
        this->mem = mem;
    }

    void work() {
        cpu->caculate();
        card->display();
        mem->storage();
    }
private:
    CPU *cpu;
    Card*card;
    Memory *mem;
};


//  实现层
class IntelCPU :public CPU{
public:
    virtual void caculate()  {
        cout << "Intelcpu 工作了" << endl;
    }
};

class NvidiaCard :public Card
{
public:
    virtual void display() {
        cout << "N卡工作了" << endl;
    }
};

class XSMem :public Memory {
public:
    virtual void storage()  {
        cout << "XS存储了" << endl;
    }
};


//高层业务
int main(void)
{
    CPU *cpu = new IntelCPU;
    Card *card = new NvidiaCard;
    Memory *mem = new XSMem;
    Computer *com = new Computer(cpu, card, mem);


    com->work();

    delete cpu;
    delete card;
    delete mem;
    delete com;
    
    return 0;
}

3.合成复用原则

如果继承和组合都能够完成功能的添加,那么优先使用组合(依赖)

合成复用原则

如图,如果想对Cat类增加功能,根据开闭原则,我们只能通过创建AdvCat类进行添加,如果使用继承,那么AdvCat在创建实例后很庞大,而且对其父类的修改也会受到影响

#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>

using namespace std;

class Cat
{
public:
    void sleep() {
        cout << " 小猫睡觉了" << endl;
    }
};

//向给猫添加一个功能, 创建一个新的猫 既能够睡觉,又能吃东西
//通过继承的方式完成
class AdvCat :public Cat{
public:
    void eatAndSleep() {
        cout << "吃东西" << endl;
        sleep();
    }
};


//使用组合的方式来添加小猫的吃东西方法
//使用组合的方式,降低了AdvCat2 和Cat的耦合度, 跟Cat的父类没有任何关系,
//只跟Cat的sleep方法有关系
class AdvCat2
{
public:
    AdvCat2(Cat *cat)
    {
        this->cat = cat;
    }

    void eatAndSleep() {
        cout << "吃东西" << endl;
        cat->sleep();
    }
private:
    Cat *cat;
};


int main(void)
{
    Cat c;
    c.sleep();

    AdvCat ac;
    ac.eatAndSleep();

    cout << "----- " << endl;

    AdvCat2 ac2(&c);
    ac2.eatAndSleep();
    
    return 0;
}

4.迪米特法则(最少知识原则)

定义:一个对象应该对其他对象有最少的了解。

通俗地讲,一个类应该对自己需要耦合或者调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂和我没有关系。

迪米特法则
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容