设计模式
设计模式共有23种,它们分为三大类:
- 创建型模式:如何创建对象。
- 结构型模式:如何实现类和对象的组合,关注类和对象的组合方式。
- 行为型模式:类或对象怎么交互以及怎么分配职责,关注对象之间如何通信。
注意:简单工厂模式不在23种之列,加上简单工厂模式共24种。
设计模式基础是多态。
设计模式的7大原则:
(目的都是为了 高内聚,低耦合)
- 单一原则(SRP):类的职责单一,对外只提供一种功能,引起变化的原因都应该只有一个。
- 开放-封闭原则(OCP):源代码的修改关闭,新功能的增加打开。也就是新加功能时只能通过新加代码来实现,不能修改源代码。
- 依赖倒转原则(DIP):依赖于抽象(接口),而不依赖与具体的实现类。也就是针对接口编程。
- 里氏代换原则(LSP):任何抽象类出现的地方,都可以用它的实现类进行替换。也就是多态。
- 接口隔离原则(ISP):一个接口只提供一种功能,不应该把所有的功能都封装到一个接口中取。也就是说不能强迫用户使用与需求功能无关的接口。
- 迪米特法则(LOD):一个对象应该尽可能的少了解其他对象,从而降低各个对象之间的耦合,提高系统的可维护性,各个模块之间相互调用时,通常会提供一个统一的接口来实现。这样一来其他模块不需要了解另外一个模块的内部实现细节,当一个模块改变时也不会影响其他模块。(黑盒原理)
- 合成复用原则(CARP):如果使用继承,会导致父类的任何变化都影响子类。如果使用对象组合,就降低了这种依赖关系。对于继承和组合优先使用组合。
重点原则代码示例
1、开闭原则(OCP):
// 开闭原则:对扩展开放,对修改关闭
class AbstractCaculator
{
public:
virtual int getResult() = 0;
virtual void setNumber() = 0;
};
// 加法类
class PlusCaculator : public AbstractCaculator
{
public:
void setNumber(int a, int b) {
this->m_a = a;
this->m_b = b;
}
virtual int getResult(){
return m_a + m_b;
}
public:
int m_a;
int m_b;
};
到这为止,第一个版本的计算器的代码写完了,如果现在要加个减法功能
第一种方法:在PlusCaculator类中新加一个成员函数进行减法操作
这很明显违背了开闭原则,修改了源码。
正确解决办法时新增加一个类继承AbstractCaculator类,再实现减法操作
减法类
class SubCaculator : public AbstractCaculator
{
public:
void setNumber(int a, int b) {
this->m_a = a;
this->m_b = b;
}
virtual int getResult() {
return m_a - m_b;
}
public:
int m_a;
int m_b;
};
这不是将简单问题复杂化了吗?没错,确实如此,但是我们这个程序简单,假设一个及其复杂的程序,源码的实现非常复杂,后期维护起来,开闭原则就能体现出它的巨大优势
2、依赖倒转原则(DIP)
传统过程设计倾向于使高层次的模块依赖于低层次的模块,抽象层次依赖于具体层次。
这样设计,高层、中层、底层的耦合度极高。很容易导致牵一发而动全身。
依赖倒转: 指的就是让中层模块依赖底层模块反转成底层依赖抽象,这样一来,高层模块依赖于抽象,底层也依赖于抽象,极大的降低了各层之间的耦合性。
代码示例:
// 银行工作人员
class BankWorker
{ // 这个类干的事情太多,违背了单一职责原则
public:
void saveService(){
cout << "办理存款业务" << endl;
}
void payService() {
cout << "办理支付业务" << endl;
}
void tranferService() {
cout << "办理转账业务" << endl;
}
};
// 业务依赖于具体工人,每个不同的业务都需要一个特定的函数
中层模块
void doSaveBussiness(BankWorker* worker) {
worker->saveService();
}
void doPayBussiness(BankWorker* worker) {
worker->payService();
}
void doTranferBussiness(BankWorker* worker) {
worker->tranferService();
}
void test01() {
BankWorker* worker = new BankWorker();
doSaveBussiness(worker); // 存款业务
doPayBussiness(worker); // 支付业务
doTranferBussiness(worker); // 转账业务
}
一般开发如同上述代码,一层依赖于一层。test01依赖于中层,中层依赖于具体实现
符合依赖倒转原则后的代码如下:
// 依赖倒转
class AbstractWorker
{
public:
virtual void doBusiness() = 0;
};
具体实现:
// 负责存款业务
class SaveBankWorker : public AbstractWorker
{
public:
virtual void doBusiness() {
cout << "办理存款业务" << endl;
}
};
// 负责支付业务
class PayBankWorker : public AbstractWorker
{
public:
virtual void doBusiness() {
cout << "办理支付业务" << endl;
}
};
// 负责转账业务
class TransferBankWorker : public AbstractWorker
{
public:
virtual void doBusiness() {
cout << "办理转账业务" << endl;
}
};
中层模块
void doNewBusiness(AbstractWorker* worker) {
worker->doBusiness();
}
void test02()
{
AbstractWorker* worker = new TransferBankWorker();
doNewBusiness(worker);
}
如此设计,具体实现类依赖于抽象,中层函数也只需要通过抽象,即使修改具体实现,doNewBusiness函数也不用修改。
3、迪米特法则(LOD)
迪米特法则又叫最小知识原则。
// 迪米特法则
class AbstractBuilding
{
public:
virtual void sale() = 0;
virtual string getQulity() = 0;
};
// 楼盘A
class BuildingA : public AbstractBuilding
{
public:
BuildingA(){
m_qulity = "高品质";
}
virtual void sale() {
cout << "楼盘A" << m_qulity << "已售出" << endl;
}
virtual string getQulity() {
return m_qulity;
}
public:
string m_qulity;
};
// 楼盘B
class BuildingB : public AbstractBuilding
{
public:
BuildingB() {
m_qulity = "低品质";
}
virtual void sale() {
cout << "楼盘B" << m_qulity << "已售出" << endl;
}
virtual string getQulity() {
return m_qulity;
}
public:
string m_qulity;
};
// 中介类
class Mediator
{
public:
Mediator() {
AbstractBuilding* buildingA = new BuildingA();
vBuilding.push_back(buildingA);
AbstractBuilding* buildingB = new BuildingB();
vBuilding.push_back(buildingB);
}
//对外提供接口
AbstractBuilding* findMyBuilding(string quality) {
for (auto it = vBuilding.begin(); it != vBuilding.end(); ++it) {
if ((*it)->getQulity() == quality) {
return* it;
}
}
return NULL;
}
~Mediator() {
for (auto it = vBuilding.begin(); it != vBuilding.end(); ++it) {
if (*it != NULL) {
delete* it;
}
}
}
public:
vector<AbstractBuilding*> vBuilding;
};
// 客户端1
void test01()
{
BuildingA* ba = new BuildingA();
if (ba->m_qulity == "低品质") {
ba->sale();
}
BuildingB* bb = new BuildingB();
if (bb->m_qulity == "低品质") {
bb->sale();
}
}
可以看到客户端1的实现访问了具体的类,如果我再加一个C类楼盘那客户端就需要修改代码
如果提供一个中介类如下客户端2,不管你再加楼盘我客户端的代码都不需要修改
这就是迪米特法则
// 客户端2
void test02()
{
Mediator* md = new Mediator();
AbstractBuilding* building = md->findMyBuilding("高品质");
if (building != NULL) {
building->sale();
}
else {
cout << "没有你需要的楼盘" << endl;
}
}
4、合成复用原则(CARP)
// 合成复用原则:能使用组合绝不使用继承
class AbstractCar
{
public:
virtual void run() = 0;
};
class Dazhong : public AbstractCar
{
public:
virtual void run() {
cout << "大众启动" << endl;
}
};
class Tuolaji : public AbstractCar
{
public:
virtual void run() {
cout << "拖拉机启动" << endl;
}
};
// 一个人针对具体的车
class PersonA : public Tuolaji
{
public:
virtual void Doufeng() {
run();
}
};
// 如果这个人想开大众了,还得再新写一个人的类
class PersonB : public Dazhong
{
public:
virtual void Doufeng() {
run();
}
};
// 上述方式就是将人类与汽车类强耦合在了一起
// 可以使用组合
class Person
{
public:
void setCar(AbstractCar* car) {
this->car = car;
}
void Doufeng() {
this->car->run();
这样一来,调用者想开什么车就传什么车就可以了,Person类一直都不用变
如此写法,人类与汽车类就没有那么强的耦合关系,即使再加几个车也是可以的
if (this->car != NULL) {
delete this->car;
}
}
public:
AbstractCar* car;
};
// 用户
void test01()
{
Person* p = new Person();
p->setCar(new Dazhong);
p->Doufeng();
p->setCar(new Tuolaji); // 想开什么开什么
p->Doufeng();
delete p;
}