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.迪米特法则(最少知识原则)
定义:一个对象应该对其他对象有最少的了解。
通俗地讲,一个类应该对自己需要耦合或者调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂和我没有关系。
迪米特法则
网友评论