前言

最近在学习一些基本的设计模式,发现很多博客都是写了六个原则,但我认为有7个原则,并且我认为在编码中思想还是挺重要,所以写下一篇博客来总结下
之后有机会会写下一些设计模式的博客(咕咕咕........

设计模式的七大原则

1.单一职责原则
2.开放-封闭原则
3.依赖倒置原则
4.里氏替换原则(LSP)
5.接口隔离原则
6.迪米特原则(最少知道原则)
7.合成复用原则

1.单一职责原则

准确解析:就一个类而言,应该仅有一个引起它变化的原因

当一个类职责变化时不会导致另一个类职责的变化.

优点:可以降低类的复杂度,提高可读性

2.开放-封闭原则

准确解析:软件实体(类,模板,函数等等)应该可以扩展,但不可修改

开闭原则是面对对象设计的核心所在;开放人员应该仅对程序中呈现出频繁变化

的那些部分做出抽象.

3.依赖倒置原则

准确解析:A.高层模板(稳定)不应该依赖底层模板(变化).两个都应该依赖抽象(稳定)
B.抽象(稳定)不应该依赖实现细节(变化).细节(变化)应该依赖抽象(稳定).

不论变化还是稳定都应该依赖于稳定

说白了:要面对接口编程,不要对实现编程.

#include<iostream>
class Book
{
public:void look()
{
....
}
.....
}
class Man
{
puclic:void Action(Book book)
{
book.look();
}
....
} int main()
{
Man man=new Man();
Book book=new book();
Man->Action(book);
....
}

上面显示的是人看书的行为

那么假设现有我想要人进行看视频行为,视频类的代码如下:

class Video
{
public:void Video()
{
....
}
.....
}

那么我不仅要对人这个类中修改,还有对主函数的代码进行修改;如果有大量的需要的话,这个修改过程将会变得非常痛苦,因为书和人的耦合度太高.

接下来使用依赖倒置原则来会解决当前的痛苦,能够降低书和人的耦合度

书和视频我们当作一个可以看的东西ILOOK作为接口类,然后书和视频继承这个类

class ILOOK
{
public:virtual void look()=0;
} class Bookpublic ILOOk
{
public:void look()
{
....
}
.....
} class Video:public ILOOk
{
public:void look()
{
....
}
.....
}
class Man
{
puclic:void Action(ILOOK ilook)
{
ilook.look();
}
....
} int main()
{
Man man=new Man();
ILOOK ilook=new book();
Man->Action(ilook);
ILOOK ilook2=new video();
Man->Action(ilook2);
....
}

这样就实现了简单的依赖倒置,人依赖于ILOOK这个类,并且书和视频也都依赖于ILook(即高层和底层都应该依赖抽象

这便是一个简单的面对接口编程.

这个依赖倒置原则将会贯串于所有设计模式,所以对于这个原则一定要有清晰的认识

4.里氏替换原则(LSP)

准确解析:子类型必须能够替换掉它们的父类型
说白了就是一种IS-A的另一种表达

比如说:鸟是一个父类,有 fly()这个虚函数,燕子是一个鸟,因为它能够飞,所以它可以继承鸟类;

企鹅不能飞,所以它不能继承鸟类,即使他在生物学上是鸟类,但它在编程世界中不能够继承鸟类

这里说出LSP的一个特点:只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能够被复用,而子类也能够在父类的基础上增加新的行为

通俗来说:子类可以扩展父类的功能,但不能改变父类原来的功能。

包括4层含义:1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2.子类中可以增加自己特有的方法。
3.当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

4种含义不展开讲,但用下面的一个例子来简单说明

#include<iostream>

class A
{
public:
int fun1(int a, int b) {
return a - b;
}
}; class B :public A
{
public:
int fun1(int a, int b) {
return a + b;
}
int fun2(int a, int b)
{
return fun1(a, b)-100; //想要a-b-100,但现实是a+b-100
}
};
int main()
{
int a = 100, b = 20;
B* m_b=new B();
std::cout << m_b->fun2(a, b) << std::endl;
}

上面显示的结果会是20,因为B类中的fun1()覆盖到了A类中的fun1();所以fun2()中调用的是B类的fun1(),这便违反了里氏替换原则

不遵循里氏替换原则的后果是:出问题的概率会大大提高

5.接口隔离原则

准确解释:不应该强迫客户程序依赖他们不用的方法;接口应该小而完备

class I
{
public:
void method1()=0;
void method2()=0;
void method3()=0;
void method4()=0;
void method5()=0;
} class A
{
public:
void depend1(I i)
{
i.method1();
}
void depend2(I i)
{
i.method2();
}
void depend3(I i)
{
i.method3();
}
}
class B:public I
{
public:
void method1()
{
std::cout<<"B实现方法1"<<std::endl;
}
void method2()
{
std::cout<<"B实现方法2"<<std::endl;
}
void method3()
{
std::cout<<"B实现方法3"<<std::endl;
}
//B类种方法4和5不是必须的
//但方法4和5因为继承的原因仍让需要空实现
void method4(){}
void method5(){}
} class C
{
public:
void depend1(I i)
{
i.method1();
}
void depend2(I i)
{
i.method4();
}
void depend3(I i)
{
i.method5();
}
}
class D:public I
{
public:
void method1()
{
std::cout<<"B实现方法1"<<std::endl;
}
void method4()
{
std::cout<<"B实现方法4"<<std::endl;
}
void method5()
{
std::cout<<"B实现方法4"<<std::endl;
}
//B类种方法2和3不是必须的
//但方法2和3因为继承的原因仍让需要空实现
void method2(){}
void method3(){}
}
上面便没有使用接口隔离原则
下面便使用了接口隔离,所以一些无关的方法就可以不用去实现

class I1
{
public:
void method1()=0;
}
class I2
{
public:
void method2()=0;
void method3()=0;
}
class I3
{
public:
void method4()=0;
void method5()=0;
}
class A
{
public:
void depend1(I1 i)
{
i1.method1();
}
void depend2(I2 i)
{
i2.method2();
}
void depend3(I2 i)
{
i2.method3();
}
}
class B:public I1,public I2
{
public:
void method1()
{
std::cout<<"B实现I1方法1"<<std::endl;
}
void method2()
{
std::cout<<"B实现I2方法2"<<std::endl;
}
void method3()
{
std::cout<<"B实现I2方法3"<<std::endl;
}
} class C
{
public:
void depend1(I1 i)
{
i1.method1();
}
void depend2(I2 i)
{
i3.method4();
}
void depend3(I2 i)
{
i3.method5();
}
}
class D:public I1,public I3
{
public:
void method1()
{
std::cout<<"B实现I1方法1"<<std::endl;
}
void method4()
{
std::cout<<"B实现I3方法4"<<std::endl;
}
void method5()
{
std::cout<<"B实现I3方法4"<<std::endl;
} }
使用接口隔离原则时应注意:
1.接口尽量小,但是要有限度。如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
2.为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。
3.提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
这个原则可以在实践多花时间思考,才可以准确地使用它

6.迪米特原则(最少知道原则)

准确解释:一个对象应该对其他对象保持最少的了解

因为类之间的关系最紧密,耦合度越高,一个类变化时对另一个类的影响也大

我们使用迪米特原则就是要降低类之间的耦合度

C++中一个重要的特性:高内聚,低耦合.高内聚,低耦合.高内聚,低耦合.(重要的事情说三遍)
#include<iostream>
#include<list>
#include<string> class Employee
{
private:
std::string m_id;
public:
Employee(){}
Employee(std::string id) :m_id(id) {}
std::string get_id()
{
return m_id;
}
};
class SubEmployee
{
private:
std::string m_id;
public:
SubEmployee() { }
SubEmployee(std::string id) :m_id(id) {}
std::string get_id()
{
return m_id;
}
};
class SubCompanyManager
{
public:
std::list<SubEmployee> getAllEmployee()
{
std::list<SubEmployee> list(100);
for (int i = 0; i < 100; i++)
{
SubEmployee emp("分公司" + std::to_string(i));
list.push_back(emp);
}
return list;
}
};
class CompanyManager
{
public:
std::list<Employee> getAllEmployee()
{
std::list<Employee> list(30);
for (int i = 0; i < 30; i++)
{
Employee emp("总公司"+std::to_string(i));
list.push_back(emp);
}
return list;
}
void printALLEmployee(SubCompanyManager sub)
{
std::list<SubEmployee> list1(100);
list1 = sub.getAllEmployee();
std::list<SubEmployee>::iterator itor= list1.begin();
for (; itor != list1.end(); itor++)
{
std::cout << itor->get_id();
}
std::list<Employee> list2(30);
list2= getAllEmployee();
std::list<Employee>::iterator itor2 = list2.begin();
for (; itor2 != list2.end(); itor2++)
{
std::cout << itor2->get_id();
}
}
}; int main()
{
CompanyManager* e = new CompanyManager();
SubCompanyManager s;
e->printALLEmployee(s);
system("pause");
return 0;
}
上面的代码违反了迪米特原则

根据迪米特法则,只与直接的朋友发生通信,而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。

class SubCompanyManager
{
public:
std::list<SubEmployee> getAllEmployee()
{
std::list<SubEmployee> list(100);
for (int i = 0; i < 100; i++)
{
SubEmployee emp("分公司" + std::to_string(i));
list.push_back(emp);
}
return list;
}
void printALLEmployee()
{
std::list<SubEmployee> list = getAllEmployee();
std::list<SubEmployee>::iterator itor = list.begin();
for (; itor != list.end(); itor++)
{
std::cout << itor->get_id();
}
}
};
class CompanyManager
{
public:
std::list<Employee> getAllEmployee()
{
std::list<Employee> list(30);
for (int i = 0; i < 30; i++)
{
Employee emp("总公司" + std::to_string(i));
list.push_back(emp);
}
return list;
}
void printALLEmployee(SubCompanyManager sub)
{
sub.printALLEmployee();
std::list<Employee> list2(30);
list2 = getAllEmployee();
std::list<Employee>::iterator itor2 = list2.begin();
for (; itor2 != list2.end(); itor2++)
{
std::cout << itor2->get_id();
}
}
};

为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。

另外切记不要过分使用迪米特原则,否则会产生大量的这样的中介和传递类,

7.合成复用原则

准确解析:尽量先使用组合后聚合等关联关系来实现,其次才考虑使用继承关系来实现

继承复用:又称"白箱""复用,耦合度搞,不利于类的扩展和维护

组合或聚合复用:又称"黑箱"复用,耦合度低,灵活度高

上面的图使用继承复合产生了大量的子类,如何需要增加新的"动力源"或者"颜色"

都要修改源代码,因为耦合度高,这违背了开闭原则

如果改为组合或聚合复用就可以很好的解决上述问题,如下图所示

七点原则总结

单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。
我们在实践时应该根据实际情况灵活使运用,才能达到良好的设计

参考博客:

http://www.uml.org.cn/sjms/201211023.asp#2

参考视频:

https://www.bilibili.com/video/av22292899

参考书籍:<<大话设计模式>>

作者:Ligo丶

出处:https://www.cnblogs.com/Ligo-Z/

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

设计模式七大原则(C++描述)的更多相关文章

  1. 图解Java设计模式之设计模式七大原则

    图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface S ...

  2. java设计模式--七大原则

    2016-05-14 20:45:38 设计模式到底是五大.六大还是七大,一直傻傻分不清楚,网上总是有说那些原则可以归为一个,我纠结了半天,突然发现自己是舍本逐末了,只要清楚这些原则的设计思想,其他的 ...

  3. 【设计模式】第一篇:概述、耦合、UML、七大原则,详细分析总结(基于Java)

    迷茫了一周,一段时间重复的 CRUD ,着实让我有点烦闷,最近打算将这些技术栈系列的文章先暂时搁置一下,开启一个新的篇章<设计模式>,毕竟前面写了不少 "武功招式" 的 ...

  4. 设计模式学习笔记(详细) - 七大原则、UML类图、23种设计模式

    目录 设计模式七大原则 UML类图 设计模式分类 单例模式 工厂设计模式 简单工厂模式 工厂方法模式(使用抽象类,多个is-a) 抽象工厂模式(使用接口,多个like-a) 原型模式 建造者模式 适配 ...

  5. java设计模式之七大原则

    java设计模式 以下内容为本人的学习笔记,如需要转载,请声明原文链接   https://www.cnblogs.com/lyh1024/p/16724932.html 设计模式 1.设计模式的目的 ...

  6. Java设计模式遵循的七大原则

    最近几年来,人们踊跃的提倡和使用设计模式,其根本原因就是为了实现代码的复用性,增加代码的可维护性.设计模式的实现遵循了一些原则,从而达到代码的复用性及增加可维护性的目的,设计模式对理解面向对象的三大特 ...

  7. 【设计模式系列】之OO面向对象设计七大原则

    1  概述 本章叙述面向向对象设计的七大原则,七大原则分为:单一职责原则.开闭原则.里氏替换原则.依赖倒置原则.接口隔离原则.合成/聚合复用原则.迪米特法则. 2  七大OO面向对象设计 2.1 单一 ...

  8. 设计模式的七大原则(Java)

    一.OOP三大基本特性 OOP 面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法.模型是用来反映现实世 ...

  9. 设计模式php+java版本(1) 基础篇 七大原则

    2019年9月6日11:15:46 关于设计模式,其实就是编程思想的一个体现,有比较完善的编程思想写出的项目代码和没有编程思想的写出的差距巨大,代码的可读性,可维护性,可扩展性天差地别,有些刚接触的编 ...

随机推荐

  1. phpstudy修改端口及网站根目录和访问 localhost 显示目录文件夹

    一.其它选项菜单=>phpStudy设置=>端口常规设置(勾选允许目录列表): 二. Apache http端口:80 网站目录:D:\phpStudy\PHPTutorial\WWW 默 ...

  2. Pycharm----显示tab制表符

    设置前: 设置后: 操作方法:

  3. docker压缩导入导出

    导出镜像 docker save <myimage>:<tag> | gzip > <myimage>_<tag>.tar.gz 导入镜像 gun ...

  4. Eclipse使用技巧小结

    前言:自学Java以来,就一直用eclipse,这款ide深受广大新手和大牛喜爱.学会使用其中的技巧,越用越熟练,开发也就越快捷方便.话不多说,直接上小结吧. 一.快捷键 1.提示 :A|t+/ 2. ...

  5. win10笔记本连接wifi出现:您的计算机配置似乎是正确的,但该配置或资源(DNS服务器)检测到有响应

    问题上图: 一直以来连接网线使用,很少使用WiFi了,在网线不好使的时候使用wifi发现并不怎么好用,甚至上不了网页,但是那时候也不怎么在意,不过一会网线就好使了所以也没处理,直到今天,因为接下来好多 ...

  6. vue 配置CDN加速

    1.首先index.html 中配置cdn 引入 <!-- 引入样式 --> <link rel="stylesheet" href="//cdn.bo ...

  7. SIGAI深度学习第七集 卷积神经网络1

    讲授卷积神经网络核心思想.卷积层.池化层.全连接层.网络的训练.反向传播算法.随机梯度下降法.AdaGrad算法.RMSProp算法.AdaDelta算法.Adam算法.迁移学习和fine tune等 ...

  8. Kafka 消息队列系列之分布式消息队列Kafka

    介绍 ApacheKafka®是一个分布式流媒体平台.这到底是什么意思呢?我们认为流媒体平台具有三个关键功能:它可以让你发布和订阅记录流.在这方面,它类似于消​​息队列或企业消息传递系统.它允许您以容 ...

  9. 【概率论】4-4:距(Moments)

    title: [概率论]4-4:距(Moments) categories: - Mathematic - Probability keywords: - Moments - Moments Gene ...

  10. 8月清北学堂培训 Day1

    今天是赵和旭老师的讲授~ 动态规划 动态规划的基本思想 利用最优化原理把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解. 更具体的,假设我们可以计算出小问题的最优解,那么我们凭借此可 ...