0. 什么是设计模式
- 设计模式是用来解决稳定中有变化的场景。
- 如果这个系统架构只有稳定没有变化,则不需要设计模式。
- 如果这个系统架构全是变化没有稳定,也无法使用设计模式。
- 将变化的成员写成抽象类或接口,将变化的方法写成抽象方法。(这样稳定的部分不会直接依赖变化的部分,而是依赖变化部分的规范(或者接口,从这里你也可以知道各个厂家定义同一接口规范的重要性))
- 在设计模式中,依赖关系是无法消除的,但是可以推迟这种依赖关系。可以将编译期静态依赖推迟到运行时动态依赖。
- 设计模式的目标是管理变化与提高复用
- 系统设计的两种手段:分解和抽象;(设计模式主要是从抽象上考虑)
1. 单一职责原则
- 尽量让一个类负责一个职责,这样既降低了类的复杂度,又解决了代码过于耦合
- 但是也会导致设计的类太多的问题。
- 这个原则是否适用视情况而定。
- 一个类应该仅有一个引起它变化的原因,这个变化的方向隐含着这个类的责任。
2. 开闭原则
- 对扩展开放,对修改关闭
- 一个类如果已经写好了,后续就不应该对这个类有任何的更改。
- 绝对不要对一个类的原有方法进行修改(因为这个类不一定是你一个人要用)。也尽量不要在原有的类上添加新方法(因为这个类不一定是你写的,可能你都没有源代码)
- 如果想添加新的方法,应该继承原有的类,在子类中添加新的方法(功能)。
- 如果要实现开闭原则,则设计一个类时,当这个类的一个功能后续可能还需要扩展时,则应该将这个类设计为抽象类或者接口,把实现推迟的子类,这样当后续要扩展这个功能时,只需要实现对应的接口即可,这样就不会修改原有的代码了。
3. 里氏替换原则
- 子类可以扩展父类功能(方法)的作用,但是不能改变父类原有功能(方法)的作用
- 对于一个功能,子类可以在保证不破坏父类原有功能的作用的前提下对这个功能进行增强。
- 即在程序中,一个地方调用父类的这个功能可以实现的,调用子类这个功能应该也能实现同样的作用(可以将效果进行增强,但是不能改变这个功能的目的)
- 因为程序中大部分通过接口或者父类引用子类,我们去判断这个功能的作用时是通过这个接口或者父类来去判断的,但是如果我们在子类更改了这个功能的作用,那么调用的时候就可能悲剧了。
4. 依赖倒转原则
- 抽象不应该依赖于细节,细节应该依赖于抽象
- 依赖倒转原则的核心思想就是面向接口编程。
- 即对于一个类的引用能用接口或抽象类就可以表达其主要功能,则就用接口或抽象类作为引用类型。
- 对于方法参数类型能用接口或者抽象类类型就可以表达其主要能够,则就用接口或抽象类的类型作为方法参数类型。
- 这样做的好处是,灵活性变大了,根据传入的实现类不同,而产生不同的效果。
5. 接口隔离原则
- 一句话,同一个接口里的所有抽象方法之间应该有很强的关联性。否则就应该将这些方法分到不同的接口中去。
- 多个专门的接口比使用单一的总接口要好。
- 一个接口代表一个角色,不应当将不同的角色交给同一个接口,没有关系的接口合并在一起会形成一个臃肿的大接口,这是对接口和角色的污染。而且会造成子类实现时还需要对不必要的方法进行实现。
- 一个类对另一个类的依赖应该建立在最小接口上。
6. 迪米特法则
- 又称为最少知道原则。一个对象应该对其他对象保持最少的了解,即只与直接朋友进行通信
- 这样做就降低了类与类之间的耦合性。
- 直接朋友:
- 成员变量,方法参数,方法返回值中的类都是直接朋友
- 局部变量中的类则不是直接朋友。
7. 合成复用原则
- 多用组合,少用继承
- 继承会破坏类的封装性。会将父类的实现细节暴露给子类。
- 使用继承会限制复用的灵活性,从父类继承来的实现是静态的,在编译时期就已经定义好了,所以无法在运行时动态变化。
- 组合和聚合是一种黑箱复用,因为细节对象的内容对客户端来说是不可见。
8. 封装变化点
- 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响,从而实现层次间的松耦合。
- 将系统中的经常变化的部分和稳定的部分隔离。
- 一侧变化,一侧稳定
补充:类之间的关系
- 继承关系
- 实现关系
- 依赖关系:方法参数引用,这种关联性比较弱
- 关联关系:强依赖关系,一个类持有另一个类的引用(作为成员变量)
- 聚合关系:关联关系的一种。整体与部分的关系。has-a关系
- 组合关系:关联关系的一种。整体与部分的关系。contains-a关系(部分脱离整体则失去意义)
设计模式的一些使用须知
- 设计模式主要是解决一些通用问题而抽离出来的一些设计思想。
- 所以设计模式在框架和各种工具包中用的最多,而应用程序则跟具体业务过于耦合,所以其实用的设计模式并不多。
- 设计模式中一句非常高频的话就是:“在不改动...的情况下,实现...的扩展。”。而这句话往往只对框架/工具包的设计才有真正的意义。
- 因为框架和工具包存在的意义就是给其他程序员使用的,而且有时候我们还需要对其进行扩展,但是这些框架和工具包又不是我们自己写的,所以我们又不能轻易去修改其源代码。所以框架和工具包的需要利用设计模式来达到上述的那句话。
- 而我们自己的应用程序,我们在应用层级别的代码都是可编辑范围的,而且不是作为基础代码去供他人使用,所以不能盲目地使用设计模式,毕竟我们在发现自己代码有问题时是可以直接修改的。