内容简介:类(实例化产生对象)是面向对象编程中最基本的组成单元,将逻辑和数据封装其中,以提高软件的重用性、灵活性和扩展性等。它相比人类社会组成,系统/子系统、组件/(微)服务、模块/包这些相当于社会中不同层次的实体或虚拟的组织机构;而类则相当于一类自然人,一个对象相当一个自然人。一个类在系统中承担着一种的 ”角色“ ,从事一种职业。大多数人只从事一种职业,也就是单一职责原则。同理若一个类只关注的就是自身职责的完成,也就是单一职责原则。面向对象设计的五个基本原则(SOLID),排在第一就是单一职责原则(SRP:Sing
理解类
类(实例化产生对象)是面向对象编程中最基本的组成单元,将逻辑和数据封装其中,以提高软件的重用性、灵活性和扩展性等。它相比人类社会组成,系统/子系统、组件/(微)服务、模块/包这些相当于社会中不同层次的实体或虚拟的组织机构;而类则相当于一类自然人,一个对象相当一个自然人。一个类在系统中承担着一种的 ”角色“ ,从事一种职业。
单一职责
大多数人只从事一种职业,也就是单一职责原则。同理若一个类只关注的就是自身职责的完成,也就是单一职责原则。
面向对象设计的五个基本原则(SOLID),排在第一就是单一职责原则(SRP:Single responsibility principle)。SRP的原话解释是:There should never be more than one reason for a class to change。应该有且仅有一个原因引起类的变更。
单一职责原则是指导”高内聚,低耦合“的基本原则,但也是最难实施的原则。
类的构建
类是什么,两个角度来看:
- 组成派:是对一类事物的抽象,由成员属性和成员方法组成的数据结构,强调封装
- 职责派:是为达成一种目标一组能力的集合,承担它所代表的抽象的职责,强调行为
上述两个角度,可能是导致两种不同的类代码结构排版的原因?
- 先属性再是方法,莫非是组成派,首先考虑是类是由哪些元素(属性)组成,再是具备哪些行为操作(方法)
- 先方法再是属性,莫非是职责派,首先考虑是类需要承担什么职责(方法),再是完成这些方法的功能需要哪些资源(属性)
当然也可能是我的无稽之谈,由于个人主要使用 Java 系语言,建议类按如下顺序组成,这只是一种编码风格:
- 类的静态常量
- 类的静态变量
- 类的成员变量
- 类的构建方法
- 类的成员方法(public->protect->private)
类的模型
以前看过 Martin Fowler
写的一篇文章叫”贫血模型”,批判贫血领域模型不够优雅、不够面向对象,提倡使用充血领域模型。若此观点应用在普通的类设计上非常有争议,至少在面向对象的语言体系中,这两种模型都存在,适用不同的场景。
贫血模型
贫血模型是指对象只有属性(getter/setter),或者包含少量的CRUD方法,而业务逻辑都不包含在其中,而是放在单独的业务处理逻辑层。JavaBean就是最为典型的代表,像Scala,Kotin在语言层次都存在数据类的概念,用于只描述数据的构成。
该模型的确是不够面向对象,对象只是作为保存状态(如数据层的表映射)或者传递状态(如方法中的出入参数)使用,所以就说只有数据没有行为的对象不是真正的对象。
在Java体系中,非常流程的就是这种设计,接口门面层(Controller)-> 业务逻辑处理层(Service)-> 数据访问层(ORM)。
充血模型
充血模型是指对象里即有数据和状态,也有行为,行为负责维持本身的数据和状态,具有内聚性,最符合面向对象的设计,满足单一职责原则。这也是我们是为常见的对象设计方式。
Martin Fowler
主张这种模型,他是从领域驱动开发(DDD)中领域模型对象来分析的,领域模型(Domain Model)是一个商业建模范畴。从一个模型的封装性来说,即有状态又有行为是合理的,但领域模型并非直接映射为单一类对象,它要比类的模型大很多,可能是由一组类聚合而成。
遵循充血模型的规范,出发点非常好,但对开发人员要求非高,随着变化与演进,最后可能一个类充满了乱七八糟的内容,反而忘记初心,违背单一原则。
类的坏味道
inFusion是一款非常不错的软件设计度量工具,它能帮助我们发现代码上坏味道。借助它分析,也讲讲inFusion中提到了哪些类的坏味道,他们违反了单一原则。
God Class
上帝类通常为过多地操作其它类的数据,从而破坏了类的封装类,上帝类从其它类中获得功能,却增加了自身的耦合性,通常会导致自己体积过大和较大的复杂度。
导致出现上帝类一般是出现在业务逻辑层,没有对逻辑层合理的分层。此类有点像八爪鱼,手上攥了东西太多,聚合太多其它对象在一个对象中直接组合所有逻辑;另一个原因是被引用的对象的封装性不好,不够内聚,暴露太多数据需要其它类来完成它自身的职责。
Blob Class
复杂类,它具有体积大(通常超过千行),高度复杂的特征。
导致出现复杂类,除了类中的方法存在行数过大的原因之外。另一个原因是一个类最早只有简单的CRUD方法,每个方法复杂度不高。后面随着需求的增加,一种场景是方法实现的场景分支越来越多,导致方法复杂度变高;另一个场景是方法个数增加太多,如Query方法,一开始只有Query1,后面不断增加Query2,Query3…以满足不同的查询条件以及响应内容等等。
Schizophrenic Class
紊乱类,一个类本应该一种抽象,完成一类责任。而该类确完成完成两种或以上的抽象,会影响类的理解和修改。特点是定义大量的接口方法,以及被不同的Client使用。
导致出现复杂类,一般出现在界面类(如Controller)、 工具 类(Util)中,即提供太多公共方法,又同时处理相应的业务逻辑。
怎么做
再回到单一职责,结合上面怎么进一步理解它,关键是职责的划分,但也是难点。
职责的划分是一定的范围与层次,比如关注的是华为手机和其它东西的区别,那华为手机就是一个整体,就是用来实现手机的功能,不是用来切水果的,所以切水果的方法,不应该实现在华为手机中。当要关注华为手机的内部结构时,那它的模块肯定是隔离的,显示屏只关注显示,通信模块只关注通信,其实每个模块都是单一职责,最终聚合在一起,就是一个手机。
在实际操作中非常难,正如上面”职责“是一个相对的概念,没有一个明确的划分原则,什么才是单一的。我们可以尝试按下面去思考一个类的设计,从多个角度来考虑,如类的组成结构,以及类的规模:
- 类自己的数据与状态的变化可能尽可能地能控制在类内部
- 变化的来源只有一类原因,原因导致变化也是围绕完成同一层次的一件事
- 类的每个方法逻辑处理足够简单,整个类的逻辑才会简单
- 类的方法数量不宜过多,个人觉得是少于15个,整个类的代码行数少于1000行左右
遵循单一职责有不少的好处:
- 可以降低类的复杂度:一个类只负责一项职责,其逻辑肯定要比负责多项职责简单。
- 提高代码的可读性:类内中复杂度降低了,容易理解,也提升了整个系统的可维护性。
- 降低变更产生的风险:变更是必然,单一职责遵守好,修改一个功能时,可以对其它功能无影响。
结语
由于职责划分无量化的标准,在实际中,我们尽量根据项目需求的不同角度去划分职责。像充血模型一样,生搬硬套单一职责原则会引起类的体积膨胀。过细的职责划分,也导致类的数量膨胀,造成整个系统的复杂。单一职责关键是要看职责的范围与层次,在一定范围内的类足够封装性,引起它的变化只有一类原因。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby on Rails社区网站开发
布拉德伯纳 / 柳靖 / 2008-10 / 55.00元
《Ruby on Rails社区网站开发》全面探讨创建完整社区网站的开发过程。首先介绍开发一个内容简单的管理系统,之后逐渐添加新特性,以创建更完整的、使用Ruby on Rails 的Web 2.0 社区网站。还给出了开发和测试中的一些建议和提示,同时指导如何使网站更生动以及维护得更好。《Ruby on Rails社区网站开发》也探讨了如何与Flickr 、Google Maps 等其他平台集成,......一起来看看 《Ruby on Rails社区网站开发》 这本书的介绍吧!