以前有专门看过设计模式相关的书籍,因为没有结合工作使用来看,所以看的多忘的也多,最近工作过程中使用了很多第三方库、框架等里面使用了很多典型的设计模式,再回头看设计模式相关的书籍感觉理解更深了。所以在这里记录一下以方便后续再温习。
设计模式
模式概括来说就是一种在特定条件下的重复问题的解决方案,这种问题是会多次重复出现,通过这种解决方案就可以复用。主要包含目标问题、约束条件、解决方法和优缺点。
模式最早是来源于建筑学,Gof四人帮将“模式”这种概念引入到了软件工程领域。并且经过长期的实践,总结出了很多经典的设计模式。并且从这些模式中又总结出了设计模式最好要遵循的六大原则
单一职责,简称SRP(Single Reponsibility Principle)
定义:应该有且仅有一个原因(职责)引起类的变更
这是最容易理解的一个原则,也是最容易不被遵守的原则原因就是这个职责不可度量。纯理论上来讲这个原则很优秀,但是现实中是会受到很多线束,像成本、人员技术水平、工期等等。除了设计模式中的demo,很难在一个已经交付的项目中找到一个满足单一职责的类。这是由于各种各样的线束导致的。
虽然满足单一职责的类很难找到,但是满足单一职责的接口和方法确实到处可见的。像观察者模式中的Observer接口,只是处理事件更新这一件事。
里氏替换,简称LSP(Liskov Substitution Principle)
定义1:如果对每一个类型为 S的对象 o1,都有类型为 T 的对象o2,使得以 T定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型S是类型T的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。
第2个定义比较通俗易懂,即只要是父类能出现的地方就可以替换成子类,并且不会影响原有功能。
这个原则涉及到类继承的特性。给类的继承特性定义了一个很好的规范,以降低类继承的侵入性、耦合性等缺点。这主要包括四个方面:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
概括来讲就是子类可以扩展父类的功能,但不能改变父类原有的功能。
注意: 如果子类不能完整地实现父类的方法或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
依赖倒置,简称DIP(Dependence Inversion Principle)
定义中包含三个方面:
高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节(实现);
细节应该依赖抽象
依赖倒置的核心就是面向接口编程;接口就类似于契约,两边只要遵守契约就可以降低耦合(具体类之间的依赖关系)。一般与里氏替换一起使用。
依赖于具体的实现叫做正置依赖,相对的不依赖具体实现 ,而是依赖于抽象就是我们这边里所说的倒置依赖
想要实现依赖倒置原则,尽量要遵循以下几个规则
- 每个类尽量都有接口或者抽象类,或者两者都有
- 变量的表面变量的表面类型(定义的类型)尽量是接口或者抽象类
- 任何类都不应该从具体类派生
- 尽量不要覆写基类的方法,这个可以结合里氏替换
接口隔离
定义:客户端不应该依赖它不需要的接口;类之间的依赖关系应该建立在最小的接口上。
简单来说就是接口尽量的细化,方法尽量的少。这个跟单一职责还是不太一样的:单一职责关心的是职责,有可能这个职责下包含了多个方法,但是一个类如果只使用其中一部分方法,这样满足了单一职责,但是违背了接口隔离原则。
接口隔离原则是对接口进行规范约束,包含以下几个方面:
- 接口尽量小,但是要满足单一职责原则,太小的话也会增加系统的复杂性
- 接口要高内聚,减少对外的交互
- 只提供访问者需要的方法,即为依赖的模块定制接口
迪米特法则(Law of Demeter, LoD),也称最少知识原则(Least Knowledge Principle, LKP)
定义:一个对象应该对其他对象有最少的了解。也即对依赖的对象知道的越少越好。
这个有点类似黑盒测试,我只需要知道提供了什么功能,怎么使用即可,不用关心内部的实现细节。
开闭原则
定义: 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。也即尽量通过扩展软件实体的功能来实现变化,而不是通过修改已有代码 。
题外话
理想是美好的,现实是骨感的;这些原则都是非常好的,但是在现实工作中使用的时候会有各种各样的原因导致违背相应的原则。不需要内疚,只要取舍合理即可。
好了原则讲完了,后面就具体讲讲软件中都会使用到哪些设计模式,当前是要结合工作来讲,不然看过之后还是印象不深刻。
网友评论