设计模式中的设计原则

设计模式中的设计原则

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;
}
上一篇:C++多态


下一篇:Ubuntu 21.04 配置自启动脚本