设计模式笔记
设计模式的本质
设计模式的本质是找到稳定和变化的分界点,如果没有稳定或没有变化设计模式也就没有意义。
编程语言和抽象思维关系
编程语言是和计算机沟通技术,抽象思维是事物表达能力。两者相辅相成。
八大设计原则
这些设计原则的目的是为了适应软件的更新迭代,以尽可能便于维护、便于理解的方式设计软件架构。此外,在C++语言中,设计模式和OOP思想是相辅相成的,继承和多态是设计模式的基础。以下的八大设计原则要比设计模式更加重要。
- 依赖倒置原则(DIP)
①高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象;
②抽象不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定);
总结:这个原则是贯穿23种设计模式的,意思是说一个类B去依赖另一个类继承体系B的时候,我们应该去依赖B的基类(抽象类),而不应该去依赖B中的子类(派生类),因为子类是变化的,如果版本迭代增加了新的B的子类,那么A的依赖代码也需要修改。但如果我们依赖的是B的基类,那么通过多态调用也可以调到新增的B的子类而无需修改A类的代码。 - 开放封闭原则(OCP)
①对扩展开放,对更改封闭;
②类模块应该是可扩展的,但是不可修改的。
总结:在面对一些新需求时,我们应该考虑如何扩展(增加)原来的代码来达到要求,而不是对原来的 西藏党性培训 www.swuplganxun.com 体系进行重写,这样能保证之前已经编译测试通过的代码不会被破坏。所以我们设计的软件架构体系应该能适应这种扩展。在写类结构体系时,应该考虑到新增需求如何扩展的问题,这就需要设计有完备接口的抽象基类被其他类来依赖,从而保证版本迭代尽可能少地改动之前地代码。 - 单一职责原则(SRP)
①一个类应该仅有一个引起他变化的原因
②变化的方向隐含着类的职责
总结:设计一个类应该明确它地责任范围,不应该去做超出范围地工作。但只有一个引起它变化地原因可能不太现实,目前有些类处理的流程可能比较复杂,会涉及很多变化和状态,但责任范围一定是明确的,这样不会导致类功能混乱,不易于管理。至于变化的方向隐含着类的职责,其实就体现在类地成员函数上,这些成员函数就是类的职责功能,而类成员数据则表现着实例对象的状态。 - Liskov替换原则(LSP)
①子类必须能够替换它们的基类(IS-A)。
②继承表达类型抽象。
总结:简单来说就是任何可以传递基类指针的地方,都可以传一个子类指针。这也是多态调用能够正常工作的必要条件,子类和基类应该时is a
的关系,子类拥有所有基类拥有的特性(包括继承的和重写的),所以可以替换。如果违背了这个原则,那么这两个类本就不应该是继承的关系,可能是应该组合的。 - 接口隔离原则(ISP)
①不应该强迫客户程序依赖它们不用的方法
②接口应该小而完备
总结:在写一个类的时候,应该把主要的接口函数写为public,不必要函数尽量写到private中,防止客户程序依赖private中的方法,导致耦合严重,不利于维护。成员函数的可访问等级有三个,设置的基本原则是:允许客户代码来访问的接口public,允许子类使用的protected,之作自身类内部使用的private,成员数据一般也应该private。 - 优先使用对象组合,而不是类继承
①类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
②继承在某种程度上继承了很多基类的函数,破坏了封装性,子类父类的耦合度过高。
③使用对象组合只要求被组合的对象具有良好的接口定义即可
总结:我的理解是不同体系的类之间一定不要使用继承,应该使用组合;使用继承的类,子类和父类中定义函数应该考虑其意义是否符合类定义的初衷,不应只考虑符合语法规则就盲目定义函数,防止破坏封装性。 - 封装变化点
①使用封装来创建对象之间的分界点,让设计者可以在分界点的一侧进行修改,而不会对另一侧产生不良影响,实现对象之间的松耦合。
总结:对象之间尽可能地解耦,不要允许彼此相互调用类或者数据进行增删改查。这样分别进行类设计不会对其他类产生影响,这个思想更多的体现在软件架构地设计中,每个模块各司其职,不要去做其他模块的工作或者修改其他模块的数据,模块与模块之间拥有确定的通讯函数接口,即分界点,能有效地避免代码混乱,便于维护。 - 针对接口编程,不要针对实现编程
①不将变量类型声明为某个特定的具体类,而声明为某个接口类;
②客户程序无需知晓对象的具体类型,只需要知道对象所具有的接口即可;
③减少系统中各部分的依赖关系,从而实现“高内聚、低耦合”的类设计方案。
总结:不仅依赖的类继承体系应该依赖抽象基类(只需要直到对象的接口即可),而且实现类的数据成员也可以是一些业务相关的接口类类型,在初始化的时候再指定为某个特定的实现类。减少系统中各部件的依赖关系,尽量不产生耦合,但肯定是需要有交互的地方,这些地方需要尽可能稳定。
23种设计模式
1、创建型模式
对象实例化的模式,创建型模式用于解耦对象的实例化过程。
单例模式:某个类智能有一个实例,提供一个全局的访问点。
工厂模式:一个工厂类根据传入的参量决定创建出哪一种产品类的实例。
抽象工厂模式:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的创建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
2、结构型模式
把类或对象结合在一起形成一个更大的结构。
装饰器模式:动态的给对象添加新的功能。
代理模式:为其它对象提供一个代理以便控制这个对象的访问。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
适配器模式:将一个类的方法接口转换成客户希望的另一个接口。
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
享元模式:通过共享技术来有效的支持大量细粒度的对象。
3、行为型模式
类和对象如何交互,及划分责任和算法。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
观察者模式:对象间的一对多的依赖关系。
仲裁者模式:用一个中介对象来封装一系列的对象交互。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
访问者模式:不改变数据结构的前提下,增加作用于一组对象元素的新功能。
模式代码示例
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
不变的算法写成父类,具体会变得参数或步骤放到子类虚函数中。
策略模式:通过虚函数替代if else
开闭原则 if else理论上是很大可能会变化的,所以通过虚函数可以替代。
状态模式: 开闭原则 虚函数 比策略模式多了状态
装饰模式: 职责单一
设计前如果用种类多用继承的方式会有几何式的增常,装饰的思想时组合的方式替代继承。
装饰模式 把子类改为装饰父类的方式。而不是创建子类。
桥模式 : 职责单一
观察者 : 依赖倒置 虚函数
观察的人是会变的的,所以程序运行中可以注册虚函数传给观察者观察。
虚函数 在c语言结构题中 可以通过函数指针实现。