你好我是程序员雪球。接下来我们学习一些经典的设计原则。其中包括SOLID,KISS,YAGNI,DRY,LOD等。其实这些设计原则从字面意思理解并不难。但是“看懂”和“会用”是两回事,而“用好”就难上加难了。
先来了解SOLID原则,他是由5个原则的首字母组成,分别是单一职责原则,开闭原则,里氏替换原则,接口隔离原则和依赖反转原则。
单一职责原则(SRP)
指的是一个类或者一个模块只负责完成一个职责(或功能)。也就是说不要设计大而全的类,要设计粒度小,功能单一的类。单一职责原则是为了实现代码高内聚,低耦合,提高代码的复用性,可读性,可维护性。但是拆得过细,反倒会降低内聚性,影响可维护性 。
如何判断类的职责是否单一?
如果类的设计出现下面的情况,这可以判断不符合单一职责:
1、类中代码行数,函数和属性过多;
2、类依赖的其他类过多,或者依赖类的其他类过多;
3、私有方法过多;
4、比较难给类起一个命名;
5、类中的大量方法都是集中操作类中的某几个属性;
开闭原则(OCP)
开闭原则指的是软件实体(模块,类,方法等)应该“对扩展开发,对修改关闭”。也就是添加一个新的功能时,应该在已有的代码基础是扩展新的模块,类,方法等,而不是修改已有代码(模块,类,方法等),所以说代码的扩展性是重点。
提高代码的扩展性有哪些方法?
多态,依赖注入,基于接口而非实现编程,以及大部分的设计模式(装饰,策略,模板,职责链,状态等)。
如何在项目中灵活应用开闭原则?
对于比较确定,短期内可能会扩展,或者需求改动对代码影响较大的情况,或者实现成本不高的扩展点,在编码代码的时候,可以事先做些扩展设计。对未来不确定,或者实现起来比较复杂的扩展点,你可以等有需求驱动的时候,再通过代码重构的方式来支持扩展的需求。代码的扩展性有时会跟可读性相冲突,你需要做好权衡。
里氏替换原则(LSP)
指的是子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。其核心思想“按照约定来设计”,这里的约定包括:函数生命要实现的功能;对输入,输出,异常的约定;甚至包括注释中所罗列的任何特殊说明。
接口隔离原则(ISP)
指的是:客户端不应该被迫依赖它不需要的接口,其中的“客户端”可以理解为接口的调用者或者使用者。“接口”可以理解为下面三种场景:一组API接口集合,单个API接口或函数,OOP中的接口概念。接下来我们一起来解读这三种场景。
一组API接口集合
可以是某个微服务的接口,也可以说某个类库的接口。如果部分接口被部分调用者使用,你可以将这部分接口隔离出来,单独给这部分调用者使用。比如app前后端分离的接口实现中,我们应该将后台管理和APP前台的接口分开定义,应该像删除这里功能一般只有后台管理才有权限,这样可以做到隔离保护,避免前台用户误删用户信息。
单个API接口或函数
部分调用者只需要函数中的部分功能,那你可以将函数拆分成粒度更细的对个函数,让调用者只依赖它需要的那个细粒度函数。函数的设计功能要单一,不要将多个不同的功能逻辑在一个函数实现,这样可以提高代码的可读性和可维护性。
OOP的接口概念。
指的是面向对象编程语言的接口语法,接口设计要单一,不要让接口的实现类也调用者,依赖不需要的接口函数。比如JAVA中的interface,假如你在项目中用了三个外部系统:Redis,MySQL,Kafka。每个系统都对应一系列配置信息,比如地址,端口,访问超时等。为了在内存中存储这些配置信息,供项目中的其他模块来使用,我们分别设计实现了三个Configuretion类:RedisConfig,MysqlConfig,KafkaConfig。这样更加灵活,易扩展,易复用。
接口隔离原则和单一职责原则的区别
单一职责原则是针对模块,类和接口设计。接口隔离原则侧重于接口的设计,它是提供了一种判断接口的职责是否单的标准,即调用者只使用部分接口或者接口功能,那接口的设计就不够单一。
依赖反转原则(DIP)
我们先来了解三个概念:控制反转(IOC),依赖注入(DI),依赖注入框架(DIF)。
控制反转(IOC)
“控制”指的是程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用了框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”到了框架。控制反转是一种比较笼统的设计思想,一般用来指导框架层面的设计,比如模板设计模式,依赖注入方式等。
依赖注入(DI)
是一种编程技巧,不通过new()的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数,构造参数等方式传递(或注入)给类使用,这样提高了代码的扩展性。
依赖注入框架(DIF)
我们通过依赖注入框架提供的扩展点,简单配置所需要的类,以及类与类之间依赖关系,就可以实现由框架来自动创建对象,管理对象是生命周期,依赖注入等原本需要程序员来做的事情。
依赖反转原则(DIP)
高层模块不依赖底层模块,高层模块和底层模块应该通过抽象来相互依赖。除此之外,抽象不依赖具体的细节,具体实现细节依赖抽象。我们拿Tomcat这个Servlet容器为例子:
tomcat是运行java web应用程序的容器。那么tomcat是高层模块,web应用就是底层模块。tomcat与web应用代码直接没有直接的依赖关系,两者都依赖同一个“抽象”,也就是Servlet规范。servlet规格不依赖具体的tomcat容器和web应用的实现细节,而tomcat容器和web应用依赖servlet规范。