1.单一职责原则2.开闭原则3.里氏代换原则4.依赖倒转原则5.接口隔离原则6.合成复用原则7.迪米特法则
1.单一职责原则一个对象的职责应该尽可能单一,并且该职责被完整地封装到一个类中
示例:
#include#include 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.里氏代换原则
所有能引用父类的地方都应该能透明地引用子类,也就说子类可以扩展父类的功能,但不能改变父类原有的功能;
典型例子:正方形不是长方形
#includeusing 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方法的,但是正方形不可以,正方形和长方形之间的继承关系不满足里氏代换原则,则正方形不是长方形。
改进后的代码:
#includeusing 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不应该是具体类,而应该是抽象基类。
代码示例:
#includeusing 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.接口隔离原则
将较大的接口进行细化,使用多个专门的接口,而不是单一的总接口
代码示例:
将霸气接口拆分为三个接口
#includeusing 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 << "nn"; Sanji sj; sj.show(); return 0; }
6.合成复用原则
当既能使用继承也能使用组合时,优先使用组合
代码示例:
#includeusing 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#include #include //抽象楼盘类 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 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; }



