七大设计原则
开闭原则:
即面向扩展开放,面向修改封闭,也就是说系统支持扩展,但是不支持修改。
为什么这么做?
在开发阶段,我们都知道,如果对一个功能进行扩展,如果只是一味地对方法进行修改,可能会造成一些问题,诸如 可 能会引入新的bug,或者增加代码的复杂度,对代码结构造成破坏、冗余,还需要重新进行全面的测试。那么该怎么解决这些问题?很简单,这就需要系统能够支持扩展,只有扩展性良好的系统,才能在不进行修改已有实现代码的基础上,引进新的功能。
我们应该怎么做?
要做到开闭原则,就需要多使用抽象类或者接口,将相似的类进行抽象,将公有的功能引入到抽象类中,这样在进行扩展时,只需要依据抽象类,生成新的子类即可。
依赖倒置原则(DIP)
即我们的client类要依赖于抽象,而不是依赖于具体,也就是我们经常听到的“要面向接口编程”。
为什么这么做?
减少类间的耦合性,提高代码的可读性和可维护性。
我们应该怎么做?
a、每个类尽量都有接口和抽象类,或者抽象类和接口都有。
b、变量的表面类型尽量是接口或者是抽象类。
c、任何类都不应该从具体类派生。(但是在做二次开发的时候,我们无法获得高层代码的时候例外),规则不是绝对的。
d、尽量不要覆写基类已经实现好的方法。
单一原则
Liskov 替换原则
即任何使用基类的地方,都能够使用子类替换,而且在替换子类后,系统能够正常工作。
为什么这么做?
采用里氏替代原则可以增强程序的健壮性,版本升级的时候可以保持非常好的兼容性,即使增加子类,原有的子类也可以继续运行。
我们应该怎么做?
在引用基类的地方就能引用子类实现
接口隔离
即应该将接口粒度最小化,将功能划分到每一个不能再分的子角色,为每一个子角色创建接口,通过这样,才不会让接口的实现类实现一些不必要的功能。
为什么这么做?
避免让接口的实现类实现一些不必要的功能
我们应该怎么做?
建立单一的接口,不要建立臃肿的庞大的接口,也就是说接口的方法尽量少。
迪米特法则
即尽量减少类之间的依赖关系.
为什么这么做?
降低类之间的耦合。
我们应该怎么做?
在应用中最直接的实现就是在两个类中间建一个中介类。但是这样可能会造成中介类的澎爆。
合成/聚合复用原则
即少用继承,多用组合。
为什么这么做?
优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
我们应该怎么做?
使用组合也就是将两个类之间建立关联关系,将一个类做为另一个类的属性。
继承与组合主要是区分两个角色之间是”is a”还是”has a”的关系,如果是”is a”就需要使用继承,而如果是”has a”就需要使用组合。
例如笔可以分为钢笔和油笔,这就是is a的关系,但是油笔与笔芯就是has a的关系。
设计模式(”设计原则 比设计模式更重要 , 遵循设计原则, 你可以创造出自己的设计模式”)
设计模式分类
类型 | 模式 | 场景 |
---|---|---|
创建型模式 | 简单工厂 | 针对同样的数据,不同的操作用同一个接口, |
工厂方法 | 针对同样的数据,不同的操作用不同的接口,定义一个拥有创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。 | |
抽象工厂 | 针对不同的数据,不同的操作用不同的接口,提供了一个创建一些列相关或相互依赖对象的接口,而无需指定它们具体的类。 | |
单例模式 | 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式(不能让一个程序打开两次 如:不能同时打开2个迅雷 迅雷用的单例模式) | |
原型模式 | 允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 | |
建造者模式 | 使得产品内部表象可以独立地变化,客户不必知道产品内部组成的细节。可以强制实行一种分步骤进行的建造过程。用一个接口完成不同的操作,需要对客户的需求进行把握。(如:登陆QQ,自动选择所在地的服务器),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 | |
结构型模式 | 适配器模式 | 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。(重用),避免上层和底层之间互相感知,将一个类的接口转换成客户希望的另外一个接口,adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |
桥接模式 | 将抽象化与实现化脱离,使得二者可以独立的变化,也就是指在一个软件系统的抽象化和实现化之间使用组合聚合关系而不是继承关系,从而使两者可以独立的变化。(相当于配电脑去装机,把各个模块组合到一起),将抽象部分与它的实现部分分离,使它们都可以独立地变化。 | |
组合模式 | 将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。(用于树状结构) | |
装饰模式 | 装饰模式以对客户端透明的方式扩展对象的功能是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 | |
外观模式 | 外部与一个子系统的通信必须通过一个统一的外观对象进行。每一个子系统只有一个外观类,而且此外观类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个外观类。(多个子系统方法都需要一个外观类统一管理,用统一的接口方便消费者使用) | |
享元模式 | 享元模式大幅度的降低内存中对象的数量,使用享元模式主要是为了优化内存,相同功能可以并行使用。 | |
代理模式 | ||
行为型模式 | 职责链模式 | 在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。(例如:晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi, 可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了) |
命令模式 | 把发出命令的责任和执行命令的责任分割开,委派给不同的对象允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。(命令模式在客户端与服务器之间用的最多 (C/S架构)) | |
解释器模式 | 给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。(如:360读取lua脚本,这个细节的实现就是解释器模式) | |
模板方法模式 | 不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。(适用于本地化,做一个软件,在日本是日文,美国是英语…) | |
迭代器模式 | 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。 | |
中介者模式 | 中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。(如:TCP/IP打洞技术) | |
备忘录模式 | ||
观察者模式 | 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。(QT的信号机制,Windows的消息机制都应用了观察者模式,还有订阅邮件,邮件到了就会给你发邮件) | |
状态模式 | 意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。(如:到了晚上12点要睡觉,到了早上8点要起床…这就是状态) | |
策略模式 | 依赖c++的多态,抽象类的指针可以访问所有子类对象,(纯虚函数),可以用一个指针访问所有策略的实现类。 | |
访问者模式 | 适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中(做任何更改不需要修改基类,不依赖虚函数) |
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 工厂模式与抽象工厂模式(Factory Pattern)(Abstract Factory Pattern):不同条件下创建不同实例
- 单例模式 (Singleton Pattern):保证一个类仅有一个实例
- 建造者模式 (Builder Pattern):将一个复杂的构建过程与其具表示细节相分离,使得同样的构建过程可以创建不同的表示
- 原型模式 (Prototype Pattern):通过拷贝原型创建新的对象
结构型模式
这些设计模式关注类和对象的组合。
- 适配器模式 (Adapter Pattern):使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
- 装饰器模式 (Decorator Pattern):保持接口,增强性能:修饰类继承被修饰对象的抽象父类,依赖被修饰对象的实例(被修饰对象依赖注入),以实现接口扩展
- 桥接模式 (Bridge Pattern):两个维度独立变化,依赖方式实现抽象与实现分离:需要一个作为桥接的接口/抽象类,多个角度的实现类依赖注入到抽象类,使它们在抽象层建立一个关联关系
- 外观模式 (Facade Pattern):在客户端和复杂系统之间再加一层,这一次将调用顺序、依赖关系等处理好。即封装底层实现,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的高层接口
- 代理模式 (Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问:增加中间层(代理层),代理类与底层实现类实现共同接口,并创建底层实现类对象(底层实现类对象依赖注入代理类),以便向外界提供功能接口
- 过滤器模式 (Filter、Criteria Pattern):使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来
- 组合模式 (Composite Pattern):用户对单个对象和组合对象的使用具有一致性的统一接口
- 享元模式 (Flyweight Pattern):享元工厂类控制;HashMap实现缓冲池重用现有的同类对象,如果未找到匹配的对象,则创建新对象
行为型模式
这些设计模式特别关注对象之间的通信。
- 责任链模式(Chain of Responsibility Pattern):拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
- 观察者模式(Observer Pattern):一对多的依赖关系,在观察目标类里有一个 ArrayList 存放观察者们。当观察目标对象的状态发生改变,所有依赖于它的观察者都将得到通知,使这些观察者能够自动更新(即使用推送方式)
- 模板模式(Template Pattern):将这些通用算法抽象出来,在一个抽象类中公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
- 命令模式(Command Pattern):将”行为请求者”与”行为实现者”解耦:调用者依赖命令,命令依赖接收者,调用者Invoker→命令Command→接收者Receiver
- 解释器模式(Interpreter Pattern):给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子
- 迭代器模式(Iterator Pattern):集合中含有迭代器:分离了集合对象的遍历行为,抽象出一个迭代器类来负责,无须暴露该对象的内部表示
- 中介者模式(Mediator Pattern):对象与对象之间存在大量的关联关系,将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散,可以独立地改变它们之间的交互
- 策略模式(Strategy Pattern):策略对象依赖注入到context对象,context对象根据它的策略改变而改变它的相关行为(可通过调用内部的策略对象实现相应的具体策略行为)
- 状态模式(State Pattern):状态对象依赖注入到context对象,context对象根据它的状态改变而改变它的相关行为(可通过调用内部的状态对象实现相应的具体行为)
- 备忘录模式(Memento Pattern):通过一个备忘录类专门存储对象状态。客户通过备忘录管理类管理备忘录类。
- 空对象模式(Null Object Pattern):创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。不要为了屏蔽null而使用空对象,应保持用null,远比用非null的值来替代“无值”要好。(慎用)
组件协作模式
Strategy 模式( 策略模式)
观察者模式
单一职责模式
装饰模式
同时继承又组合一个类
桥接模式
对象创建模式
工厂模式
绕开细节依赖,不依赖具体类
抽象工厂