设计模式中的设计原则
1.单一职责原则
一个对象的职责应该尽可能单一,并且该职责被完整地封装到一个类中
示例:
#include <iostream>
#include <string>
class Carnivore
{
public:
void show(const std::string& animal)
{
std::cout << animal << " 是食肉动物!" << std::endl;
}
};
class Herbivore
{
public:
void show(const std::string& animal)
{
std::cout << animal << " 是食草动物!" << std::endl;
}
};
void test()
{
Carnivore a1;
Herbivore a2;
a1.show("狼");
a1.show("老虎");
a2.show("羊");
a2.show("牛");
}
int main()
{
test();
return 0;
}
2.开闭原则
一个软件实体应该对扩展开放,对修改关闭,也就是说在不修改原有代码的前提下,扩展新的模块。开闭原则是最重要和最基础的设计原则。
在C++中的具体实现是通过定义一个抽象类,在抽象类中定义接口,扩展新模块时继承这个抽象类,并重写纯虚函数,进行具体实现;
示例:
//抽象计算器类
class AbstractCaculator{
public:
virtual void setOperatorNumber(int num1, int num2) = 0;
virtual int getResult() = 0;
};
//加法计算器类
class PlusCaculator:public AbstractCaculator{
public:
virtual void setOperatorNumber(int num1, int num2)
{
_num1 = num1;
_num2 = num2;
}
virtual int getResult()
{
return _num1 + _num2;
}
private:
int _num1;
int _num2;
};
//减法计算器类
class MinusCaculator :public AbstractCaculator{
public:
virtual void setOperatorNumber(int num1, int num2)
{
_num1 = num1;
_num2 = num2;
}
virtual int getResult()
{
return _num1 - _num2;
}
private:
int _num1;
int _num2;
};
//乘法计算器类
class MutiplyCaculator :public AbstractCaculator{
public:
virtual void setOperatorNumber(int num1, int num2)
{
_num1 = num1;
_num2 = num2;
}
virtual int getResult()
{
return _num1 * _num2;
}
private:
int _num1;
int _num2;
};
void test()
{
//进行加法运算
AbstractCaculator* p = new PlusCaculator();
p->setOperatorNumber(2, 10);
std::cout << "result:" << p->getResult() << std::endl;
delete p;
//进行乘法运算
p = new MutiplyCaculator();
p->setOperatorNumber(5, 6);
std::cout << "result:" << p->getResult() << std::endl;
delete p;
}
3.里氏代换原则
所有能引用父类的地方都应该能透明地引用子类,也就说子类可以扩展父类的功能,但不能改变父类原有的功能;
典型例子:正方形不是长方形
#include <iostream>
using namespace std;
//长方形
struct Rectangle
{
int _length; //长
int _width; //宽
virtual void setLength(int l)
{
_length = l;
}
virtual void setWidth(int w)
{
_width = w;
}
int getLength()
{
return _length;
}
int getWidth()
{
return _width;
}
};
//正方形
struct Square : public Rectangle
{
virtual void setWidth(int w)
{
_length = w;
_width = w;
}
virtual void setLength(int l)
{
_length = l;
_width = l;
}
};
void reSize(Rectangle* rct)
{
//当宽度小于等于长度,宽度+1
while (rct->_width <= rct->_length)
{
rct->setWidth(rct->_width+1);
}
}
void show(Rectangle* rct)
{
cout << "长度为: " << rct->_length << endl;
cout << "宽度为: " << rct->_width << endl;
}
int main()
{
Rectangle* rt = new Rectangle();
rt->setLength(15);
rt->setWidth(10);
reSize(rt);
show(rt);
delete rt;
cout << "***********************************************" << endl;
rt = new Square();
rt->setWidth(10);
reSize(rt);
show(rt);
delete rt;
return 0;
}
代码分析:
长方形因为宽度小于长度,所以在调用reSize方法时会让宽度+1,直到宽度大于长度;
而正方形的长度和宽度相等,在调用reSize方法时,让宽度+1的同时,长度也+1,长度和宽度一直相等,所以陷入死循环,直到系统溢出;
长方形是可以使用reSize方法的,但是正方形不可以,正方形和长方形之间的继承关系不满足里氏代换原则,则正方形不是长方形。
改进后的代码:
#include <iostream>
using namespace std;
//四边形
struct Quadrilateral
{
//获取长度
virtual int getLength() = 0;
//获取宽度
virtual int getWidth() = 0;
};
//长方形
struct Rectangle : public Quadrilateral
{
int _length; //长
int _width; //宽
void setLength(int l)
{
_length = l;
}
void setWidth(int w)
{
_width = w;
}
//重写纯虚函数
virtual int getLength()
{
return _length;
}
virtual int getWidth()
{
return _width;
}
};
//正方形
struct Square : public Quadrilateral
{
int _side;
void setSide(int side)
{
_side = side;
}
int getSide()
{
return _side;
}
//重写纯虚函数
virtual int getLength()
{
return _side;
}
virtual int getWidth()
{
return _side;
}
};
void reSize(Rectangle* rct)
{
//当宽度小于等于长度,宽度+1
while (rct->_width <= rct->_length)
{
rct->setWidth(rct->_width + 1);
}
}
void show(Quadrilateral* q)
{
cout << "长度为: " << q->getLength() << endl;
cout << "宽度为: " << q->getWidth() << endl;
}
int main()
{
Rectangle* rt = new Rectangle();
rt->setLength(15);
rt->setWidth(10);
reSize(rt);
show(rt);
delete rt;
return 0;
}
代码分析:
将长方形和正方形*有的部分设置到基类中,让这两个类继承基类并重写基类的方法,每个类中实现各自特有的方法。打印长宽的接口show的参数设置为抽象基类,可以通过根据实际数据类型来输出对应的宽度和高度,但是reSize接口只能传递Rectangle对象的指针,不能传递Square对象的指针,因此保证了程序的安全性,程序没有违背里氏代换原则。
4.依赖倒转原则
代码要依赖于抽象类,而不是具体类;要针对接口或抽象类编程,而不是针对具体类编程;
在C++中具体来说就是一个类A中的数据成员为另一个类对象B,这个类B不应该是具体类,而应该是抽象基类。
代码示例:
#include<iostream>
using namespace std;
//传统的过程式设计倾向于高层次的模块依赖于底层的模块
//依赖倒转原则:业务层依赖于抽象层,依赖多态实现
class AbstractWorker{
public:
virtual void doBusiness() = 0;
};
//每个类只完成一个功能
class payBankWorker :public AbstractWorker{
public:
virtual void doBusiness()
{
cout << "办理支付业务" << endl;
}
};
class saveBankWorker :public AbstractWorker{
public:
virtual void doBusiness()
{
cout << "办理存款业务" << endl;
}
};
class transferBankWorker :public AbstractWorker{
public:
virtual void doBusiness()
{
cout << "办理转账业务" << endl;
}
};
//中间类调用抽象类,可以使业务具有可扩展性
void doService(AbstractWorker* worker)
{
worker->doBusiness();
delete worker;
}
//更高级的类调用中间类
void test()
{
doService(new payBankWorker);
doService(new transferBankWorker);
doService(new saveBankWorker);
}
int main()
{
test();
return 0;
}
5.接口隔离原则
将较大的接口进行细化,使用多个专门的接口,而不是单一的总接口
代码示例:
将霸气接口拆分为三个接口
#include <iostream>
using namespace std;
//接口隔离原则
//霸王色霸气
struct OverlordColor
{
virtual void run1() = 0;
};
//见闻色霸气
struct SeeingAndHearingColor
{
virtual void run2() = 0;
};
//武装色霸气
struct ArmedColor
{
virtual void run3() = 0;
};
//路飞
class Luffy : OverlordColor, SeeingAndHearingColor, ArmedColor
{
private:
virtual void run1()
{
cout << "\t霸王色霸气" << endl;
}
virtual void run2()
{
cout << "\t见闻色霸气" << endl;
}
virtual void run3()
{
cout << "\t武装色霸气" << endl;
}
public:
void show()
{
cout << "路飞,霸气面板如下:" << endl;
run1();
run2();
run3();
}
};
//山治
class Sanji : SeeingAndHearingColor, ArmedColor
{
private:
virtual void run2()
{
cout << "\t见闻色霸气" << endl;
}
virtual void run3()
{
cout << "\t武装色霸气" << endl;
}
public:
void show()
{
cout << "山治,霸气面板如下:" << endl;
run2();
run3();
}
};
int main()
{
Luffy lf;
lf.show();
cout << "\n\n";
Sanji sj;
sj.show();
return 0;
}
6.合成复用原则
当既能使用继承也能使用组合时,优先使用组合
代码示例:
#include<iostream>
/*
合成复用原则:继承和组合优先使用组合
*/
using namespace std;
//抽象车类
class AbstractCar{
public:
virtual void run() = 0;
};
//大众车类
class Dazhong :public AbstractCar{
public:
virtual void run()
{
cout << "大众启动中....." << endl;
}
};
//奔驰类
class BenChi:public AbstractCar{
public:
virtual void run()
{
cout << "奔驰启动中....." << endl;
}
};
//五菱荣光类
class WuLingRongGuang :public AbstractCar{
public:
virtual void run()
{
cout << "车中之王,五菱荣光,震撼登场,启动中....." << endl;
}
};
当使用继承时,人想开不同的车就需要继承不同的具体的车类
针对具体类,不适合使用继承
//class PersonA :public Dazhong{
//public:
// void DouFeng()
// {
// run();
// }
//};
//
//class PersonB :public BenChi{
//public:
// void DouFeng()
// {
// run();
// }
//};
//void test()
//{
// PersonA p;
// p.DouFeng();
// PersonB p2;
// p2.DouFeng();
//}
//给人增加一个车的属性
class Person{
public:
void setCar(AbstractCar* car)
{
_car = car;
}
void DouFeng()
{
_car->run();
if (_car != NULL)
{
delete _car;
_car = NULL;
}
}
AbstractCar* _car;
};
void test()
{
Person a;
a.setCar(new WuLingRongGuang);
a.DouFeng();
a.setCar(new Dazhong);
a.DouFeng();
}
int main()
{
test();
return 0;
}
7.迪米特法则
一个软件实体应该尽可能少地和其他实体发生相互作用,也就是只和直接接触的实体建立调用关系
迪米特法则,也叫最少知道原则。
就是说一个对象应当对其它对象有尽可能少的了解,不要和陌生人说话。
主张通过一个中介类获取用户的需求,并进行服务,
用户不需要自己进行繁琐的工作,中介类会替用户做这些事
迪米特法则的初衷在于降低类之间的耦合
代码示例:
#include<iostream>
#include<string>
#include<vector>
//抽象楼盘类
class AbastractBuilding{
public:
virtual void sale() = 0;
virtual std::string getQuality() = 0;
};
//楼盘A
class BuildingA :public AbastractBuilding{
public:
BuildingA()
{
_quality = "高品质";
}
virtual void sale()
{
std::cout << "楼盘A--" << _quality << " 正在售卖中!" << std::endl;
}
virtual std::string getQuality()
{
return _quality;
}
public:
std::string _quality;
};
//楼盘B
class BuildingB :public AbastractBuilding{
public:
BuildingB()
{
_quality = "中品质";
}
virtual void sale()
{
std::cout << "楼盘B--" << _quality << " 正在售卖中!" << std::endl;
}
virtual std::string getQuality()
{
return _quality;
}
public:
std::string _quality;
};
//楼盘C
class BuildingC :public AbastractBuilding{
public:
BuildingC()
{
_quality = "低品质";
}
virtual void sale()
{
std::cout << "楼盘C--" << _quality << " 正在售卖中!" << std::endl;
}
virtual std::string getQuality()
{
return _quality;
}
public:
std::string _quality;
};
客户端:当需要不同品质的楼盘,要和具体类打交道,需要通过一次次的查找比较,
找到自己想要的楼盘,效率很低,客户体验很差
//void Client01(std::string quality)
//{
// BuildingA* p = new BuildingA;
// if (p->_quality == quality)
// {
// p->sale();
// delete p;
// return;
// }
//
//
// BuildingB* p2 = new BuildingB;
// if (p2->_quality == quality)
// {
// p2->sale();
// delete p2;
// return;
// }
//
//
// BuildingC* p3 = new BuildingC;
// if (p3->_quality == quality)
// {
// p3->sale();
// delete p3;
// return;
// }
//
// std::cout << "不好意思,没有满足您要求的楼盘!" << std::endl;
//}
//中介类:维护管理所有的楼盘类
class Intermediary{
public:
Intermediary()
{
AbastractBuilding* p = new BuildingA;
vbuilding.push_back(p);
p = new BuildingB;
vbuilding.push_back(p);
p = new BuildingC;
vbuilding.push_back(p);
}
AbastractBuilding* findMyBuilding(std::string quality)
{
for (const auto e : vbuilding)
{
if (e->getQuality() == quality)
return e;
}
return NULL;
}
~Intermediary()
{
for (const auto e : vbuilding)
{
delete e;
}
}
private:
std::vector<AbastractBuilding*> vbuilding;
};
//客户通过中介得到自己想要的楼盘
//不直接和楼盘主交涉
void Client02()
{
Intermediary* midPeople = new Intermediary;
AbastractBuilding* building=midPeople->findMyBuilding("高品质");
if (building != NULL)
{
building->sale();
}
else
{
std::cout << "对不起,没有找到满足您要求的楼盘!" << std::endl;
}
}
int main()
{
Client02();
return 0;
}