如何做详细设计之设计原则

旧文重发,转自我的博客,本篇文章写于2014年12月21日

Picture: from Instagram

今天我们来讨论一下设计原则,在详细设计的过程中,需要遵守一些设计原则,本篇的设计原则都是我自己经常思考总结,有可能有些不全,如果你已经能够很好的掌握和利用面向对象的程序设计原则,你可以不看本文。本文主要是在自己工作中的实践与体会,不讲六大原则。当然面向对象程序设计的六大原则,是我们在详细设计中要努力达到的目标。但是又有多少人望而兴叹呢。

单一职责

可能有人看到这个时,感觉我开头的话在忽悠,单一职责不就是面向对象程序设计的六大原则之一吗?是的,单一职责是六大原则之一,但是我还是要讲,我们这里分开讲,类的单一职责,方法的单一职责。

类的单一职责:类的单一职责,就是讲一个类专注于做某一件事。我还拿我们做的web业务系统说事,比如我们有一个接口类UserArchiveService,从名字看是一个用户档案业务类,但是哪天来了一个“简单”的需求。需要获取一个用户的班级信息,显然不是属于UserArchiveService类的需求,但是我们当时没有类似的类,想就这一个临时小接口,加在UserArchiveService算了,那么问题就来了,你违反了类的单一职责的原则。这样发展下去,别人也看不懂这个类到底是干嘛的了。

那么,怎么才能是职责单一了呢?

当然这需要看场景,比如上面的UserArchiveService如果你正是一个面向用户的系统,里面会有很多的方法或者实现比较复杂,都属于UserArchiveService,这个类也会特别臃肿,即使都是为UserArchiveService服务也不能说这个类也就职责单一了。

需要再次拆分。如何拆分呢?

当然需要根据业务场景拆分(我又在说废话了),需要根据自己的业务特点思考。我举个我遇到的例子,前篇我说过一个阅卷模块,阅卷模块有个阅卷的业务,这个业务是这个模块的核心,如果划分为一个MarkingService,接口方法较多,而且部分方法实现叫复杂,这个时候一个MarkingService肯定不行了,这时候我们就需要更加详细的设计,把阅卷模块的业务再次拆分,我们当时的设计为,MarkingService作为门面接口,然后把阅卷拆分为阅卷任务、阅卷会话、阅卷历史、阅卷池等类,做到职责单一。

当然类的职责单一是相对的,不是绝对的,需要看设计者对业务的理解和把控,有时也不好控制。作为一个经验丰富的设计者,可能这方面做的较好,当然如果对于像我这样的菜鸟,如何做到把控好职责单一呢?后面我们有重构以及理解业务的本质来解决这个问题。这也是我把重构放在这个系列的原因。

方法的单一职责:方法的单一职责,就是讲一个方法专注于实现一个功能业务。

方法职责的单一,如何衡量方法职责的单一呢?很简单,用一句话说明白你的方法用意。在也很好发现,如果你是一个对自己严格要求的设计者,在你写注释的时候就会发现。当然即使你在刚开始设计的时候方法职责做到了单一,但是你如何保证在需求变更时,在修复缺陷时,保证方法的职责单一呢?当然还是要靠自己对方法的严格把控,比如方法设计出来之后,除非特殊情况,不允许变更参数。

让使用者用着“爽”

这个原则在面向对象设计的六大原则中没有。现在在做互联网产品中,用户体验是我们经常提到的,就是让用户用着爽,我们作为设计者,使用者就是我们的用户,我们也要强调用户体验。当然我们实际工作中,使用者能够一次调通,不抛异常已经很高兴了。我把这个作为一个设计原则,因为我认为这个是非常重要的。是我们努力的方向。那么如何让用户用着爽呢?首先,基础是规范,这个可以参考上一篇如何做详细设计之规范。做到了规范,然后才有下文。

考虑类的使用方法,比如你在设计一个类,调用者在80%的时候只需要传其中的三个参数设值,那么你为什么不增加一个构造器呢,让使用者一句话就可以完成对象的创建呢。

善用重载,这不但是让接口用着爽的一种方法,也是我们在设计时非常值得利用的一种方法。比如我们同一个业务,有时某个值为null,是一种处理方式,不为null为另一种方式,这时我们需要考虑使用重载,而且是尽量使用重载。

麻烦留给自己,比如某些业务方法是一个原子,而且调用顺序都是一致的,这时我们就可以封装一个门面来做这个事,调用者只需调用一个方法就可以解决,这也是在业务比较复杂的时候,需要考虑使用门面设计模式的原因。

减少使用者的学习成本,当然这是一个愿景,我们以上做的很多事,都在为减小使用者的学习成本而努力。我们在设计时,需要也是需要考虑的一件事。

下面一个原则是针对具有数据持久层的系统。而且不是以数据库设计为驱动设计的系统。如果以数据库为驱动设计的系统,以下原则不适合。

轻量的数据持久层

在不是以数据库设计为驱动的设计的系统中,数据持久层就可以把它看做一个数据访问的通道,没有任何的业务逻辑,数据持久层的接口完全可以屏蔽使用什么数据库,几乎所有的接口方法就是对数据的增删改查。在实现时,如果为关系型数据库,sql非常简单。没有复杂的表的连接或者子查询。做到轻量级的持久化层做到如下几点:

实现足够简单,几乎没有if else,for等逻辑判断(包括sql中)

几乎所有sql或者查询对象中的参数都是由调用者提供。

没有复杂的数据组合,比如没有复杂的sql表的连接或者子查询,最好是单表操作,控制多表连接的数量。

如果以上三点做不到,可以从数据库设计或者详细设计中找下原因。

轻量的数据持久层好处:

灵活便于接口复用。

对于后期性能优化有利。越简单越容易优化。

便于数据库迁移。

方便以后做分库分表。

….

轻量的数据持久层坏处:

多次查询造成担心性能问题

业务层代码量增加

解决方式:对于第一个可以在数据库设计时的反范式中做些处理,另外还可以综合利用缓存等手段解决。第二个问题,只能用敲键盘解决。

理论和实践

本篇中的原则,没有写明从如何使类的灵活的角度去考虑,设计原则,其实这些是保持类的灵活的基石,如果以上做不好,类可能做到灵活。也没有从类的六大设计原则上入手,类的六大设计原则是理论指导我们的设计,本篇主要是从实践的总结,本篇的一些做法,也都能从类的设计原则中寻到理论依据。详细设计就是需要不断的思考,没有错与对,只有合适和不合适。而且到底合不合适需要经过时间的检验。

本篇内容是在平常工作中的实践思考与总结。如有不妥之处,欢迎讨论。

关注本公众号回复“详细设计”,查看全部系列内容

欢迎分享给你的朋友

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180220G0FRJ200?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券