“为什么类的职责要单一化?
“类的职责单一化很容易吗?
首先,我要提醒一下看到这篇文章的同学,我认为保证类(一定是类吗?)的单一职责并不容易
软件开发过程中,自古就流传着几大规则,无论如何这里都要和大家阐述一遍
单一职责原则
一个类应该只有一个发生变化的原因
开闭原则
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。
里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象,换句话说,子类在任何引用基类的地方都可以替换成子类。
依赖倒置原则
这个原则说的详细一点其实可以概括为两点:
高层模块不应该直接依赖于底层模块,应该依赖于抽象
抽象不应该依赖于具体实现,具体实现应该依赖于抽象
接口隔离原则
程序不依赖于不使用的接口,换句话说,一个程序只依赖于它需要的接口。
单纯从概念上讲呢,单一职责原则可算是最简单易懂的一种原则了,就像设计模式中的单例模式一样无趣,是这样吗?
谁的职责
说实话,看过不少讲解“职责单一”设计原则的文章,都是以类来阐述。其实我觉得不对,职责单一设计原则本质上是软件设计原则的一种思想,具有指导意义。至于谁的职责需要单一,是一个伪命题,不仅仅指面向对象编程中的类,系统的模块,甚至于微服务在架构设计中也应该遵循此规则。
在面向对象设计的理解中,程序最基本的组成单位是类(class),多个类组成模块(module),多个模块组成服务(service),多个服务组成系统(system),一般的软件系统都会存在以上几个概念。
无论是类,还是模块,还是服务,还是系统,我认为设计的时候都要保证“单一职责”。
单一真的容易吗
说到“单一”职责,每个人都有不同的看法
- class UserInfo
- {
- //用户id
- public int UserId{get ;set ;}
- //用户登录账号
- public string Account{get;set ;}
- //用户登录密码
- public string Pwd{get ;set;}
- //用户姓名
- public string Name{get ;set ;}
- }
以上是最常见的用户信息实体,你认为它职责单一吗?说一说,我自己的看法:
站在用户信息的角度来说,这个类代表的是用户信息,它就是单一的,这也是大多数人的看法,有错吗?其实没错。因为在当前场景下,它确实是这样。
随着业务的发展,用户的信息字段会越来越多,比如:用户的年龄,性别,学历....等等。看着越来越大的UserInfo类,是否该拆分呢?
这个时候我觉得你可以根据用户信息的类型来进行拆分,毕竟大而全的类其实并不好。怎么拆分呢?比如:可以根据用户登录场景拆分出用户认证的类型
- class UserAuth
- {
- //用户id
- public int UserId{get ;set ;}
- //用户登录账号
- public string Account{get;set ;}
- //用户登录密码
- public string Pwd{get ;set;}
- }
可以根据用户信息在系统中的出现频率和重要度拆分出用户基本信息和用户扩展信息
- class UserBasicInfo
- {
- //用户id
- public int UserId{get ;set ;}
- //用户姓名
- public string Name{get ;set ;}
- //用户手机号
- public string Phone{get ;set ;}
- //其他基本属性
- }
- class UserExtendnfo
- {
- //用户邮箱
- public string Email{get ;set ;}
- //用户QQ号
- public string QQ{get ;set ;}
- //其他属性
- }
当然这里我只是举个栗子,如果用户的Email和手机号一样常用,可以把Email属性提到基本属性中。
以上只是以用户信息为例,根据不同的用途进行拆分的一个栗子。在不同的业务背景下,不同的业务阶段,对同一个类的拆分可能会有很大不同。有的时候,你所认为的"正确“会随着系统的发展慢慢变成”错误“,当然这种”错误“并不可怕,毕竟系统的架构都是慢慢迭代出来的。
总之呢,评价一个类是否一定满足单一原则,并没有一个统一标准和规范,在实际的开发中,也没有必要进行过度设计,在项目初级,完全可以是一个满足业务需求的大而全的类,随着业务的发展,你必然会经历拆分的过程,这也是软件发展的必然阶段。
以上只是针对类这个最基本的面向对象单位来聊了聊,上升到模块以及系统也是一样的道理,微服务也是随着软件开发的不断演进而出现的,其实从职责上来看,微服务也是职责单一原则的产物,而这个这则单一更多的是倾向于业务单一性,并非功能单一性。
那职责拆分的越细越好吗?我不这么认为,当一个类或者模块甚至系统,被拆分过细的时候,就会面临着维护的问题,拿微服务来说,当微服务的数量过多,就会面临着治理等一系列问题,这也是K8s要解决的问题之一。
拆分原则
说到底,虽然职责单一很难在主观上给予准确判断,但是还是有一些通用规则可以借鉴,这里以类为例
- 高内聚。系统在修改任一功能的时候,只需要修改一处地方,如果你需要修改多处才能满足某个需求,很有可能你的职责划分的不合理
- 属性过多。当一个类属性过多的时候,可以考虑把这个类进行职责的拆分。而至于多少个才算多呢?当查找某个属性令你头疼的时候,说明已经到了可以拆分的程度了(自己杜撰)
- 依赖过多。当一个类型中依赖的资源过多的时候,可以进行拆分
- 独立变化。当一个类的某些属性被大量使用而且会经常变化的时候,可以考虑把这些属性进行拆分成独立的类。
说到职责单一,这里顺便提一下接口的设计,接口的设计更要遵循职责单一的原则,接口本质上是对业务的抽象,不同的业务应该抽象成不同的接口,以保证每个类,每个模块,每个系统都可以独立扩展。
写在最后
没有绝对好的方法可以让所有人都认为你的拆分是正确的“职责单一”,有的时候,怎么样才能职责单一真是要靠“灵感”
本文转载自微信公众号「架构师修行之路」,可以通过以下二维码关注。转载本文请联系架构师修行之路公众号。