这里写目录标题
理论一:对于单一职责原则,如何判断某个类的职责是否够单一
如何理解单一职责原则(SRP) ?
SOLID原则并非单纯的1个原则,而是由5个设计原则组成,他们分别是:单一职责原则,开闭原则,里式替换原则,接口隔离原则和依赖反转原则,依次对应SOLID中的S,O,L,I,D这五个英文字母
单一职责原则的英文是Single Responsibility Principle 缩写SRP . 意思是一个类或者模块只负责完成一个职责(或者功能)
一个类只负责完成一个小职责或者功能.不要设计大而全的类,要设计粒度小,功能单一的类. 单一职责原则是为了实现代码高内聚,低耦合,提高代码的复用性,可读性,可维护性.
如何判断类的职责是否够单一
不同的应用场景,不同阶段的需求背景,不同的业务层面,对同一个类的职责是否单一,可能会有不同的判断结果.一些侧面的判断指标更具有直到意义和可执行性,比如,出现下面这些情况就有可能说明这类的设计不满足单一职责原则:
- 类中的代码行数,函数或者属性过多.
- 类依赖的其他类过多,或者依赖类的过多.
- 私有方法过多;
- 比较难给类起一个合适的名字;
- 类中大量的方法都是集中操作类中的某几个属性
类的职责是否涉及得越单一越好
单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性.同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚,低耦合.但是,如果拆的过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性
理论二:如何做到"对扩展开放,修改关闭?扩展和修改各指什么?"
1.如何理解"对扩展开放,对修改关闭"?
添加一个新的功能,应该是通过在已有的代码基础上扩展代码(新增模块,类,方法属性等),而非修改已有的代码(修改模块,类,方法属性等)的
方式来完成.关于定义,我们要注意两点.第一点事,开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发.第二点是,统一的代码改动,在粗代码粒度下,可能被认定为"修改";在细代码粒度下,可能又被认定为扩展.
2.如何做到"对扩展开发,修改关闭"
我们要时刻具备扩展意识,抽象,封装意识.在写代码的时候,要多花点时间思考,这段代码未来可能有哪些需求变更,如何设计代码结果,事先留好扩展点,以便在未来变更的时候,在不改动代码整体结构,做到最小代码改动的情况下,将新的代码灵活的插入到扩展点行
总结:对扩展开放是为了应对变化(需求),对修改关闭时为例保证已有代码的稳定性;最终结果是为了让系统更有弹性
理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
LSP描述:
子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不背破坏
哪些代码明显违背了LSP?
1.子类违背了父类声明要实现的功能
例如:父类实现金额的排序是根据金额的大小,而子类是根据时间排序,那么子类的设计就违背了里式替换原则
2.子类违背了父类对输入,输出,异常的约定
在父类中,某个函数约定:运行出错的时候返回 null;获取数据为空的时候返回空集合(empty collection)。而子类重载函数之后,实现变了,运行出错返回异常(exception),获取不到数据返回 null。那子类的设计就违背里式替换原则。
3.子类违背了父类注释中所罗列的任何特殊说明
父类中定义的 withdraw() 提现函数的注释是这么写的:“用户的提现金额不得超过账户余额……”,而子类重写 withdraw() 函数之后,针对 VIP 账号实现了透支提现的功能,也就是提现金额可以大于账户余额,那这个子类的设计也是不符合里式替换原则的。
总结: 个人理解里式替换原则是子类完美继承父类的设计初衷,并做了增强(下面是具体的)
里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。
理解这个原则,我们还要弄明白里式替换原则跟多态的区别。虽然从定义描述和代码实现上来看,多态和里式替换有点类似,但它们关注的角度是不一样的。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,用来指导继承关系中子类该如何设计,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性。
理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
如何理解“接口隔离原则”?
客户端不应该被强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者.
如果把“接口”理解为一组接口集合,可以是某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口。
如果把“接口”理解为单个 API 接口或函数,部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数。
如果把“接口”理解为 OOP 中的接口,也可以理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。
2. 接口隔离原则与单一职责原则的区别
单一职责原则针对的是模块、类、接口的设计。接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
1. 控制反转
实际上,控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
2. 依赖注入
依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
3. 依赖注入框架
我们通过依赖注入框架提供的扩展点,简单配置一下所有需要的类及其类与类之间依赖关系,就可以实现由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。
4. 依赖反转原则
依赖反转原则也叫作依赖倒置原则。这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。
“基于接口而非实现编程”跟“依赖注入”,看起来非常类似,那它俩有什么区别和联系呢?
“基于接口而非实现编程”与“依赖注入”的联系是二者都是从外部传入依赖对象而不是在内部去new一个出来。
区别是“基于接口而非实现编程”强调的是“接口”,强调依赖的对象是接口,而不是具体的实现类;而“依赖注入”不强调这个,类或接口都可以,只要是从外部传入不是在内部new出来都可以称为依赖注入。
待续…