思考的轨迹

人若无名 专心练剑

软件设计的几点思考和总结

| Comments

软件设计是不易的,尤其要设计出结构良好、可靠性高、又不失可扩展性和可维护性的软件更是困难的事。

随着项目的积累、经验的丰富,不同阶段,自己对设计的理解也是不一样的,就像看一本好书,每读一遍,对其中内容的理解往往也会不同一样。

下面是自己对如何设计良好软件的一点理解:

1.模块/类要尽可能只做一件事,并且要把事做好(符合单一职责原则、高内聚低耦合),这样也有利于提高重用性(KISS原则、DRY原则)

模块/类设计的注意点:

  • 模块所处层次是否分明[分层设计(横行划分)],功能是否清晰[模块化设计(纵向划分)、正交设计]

  • 模块接口要仔细设计

接口要提供哪些功能,接口函数/参数命名是否直达其意(命令查询分离原则),有什么限制,如何返回结果,是否易用,尽可能多从用户使用角度考虑接口的设计;

接口设计要符合“接口隔离原则”,大而全的接口往往没有多个功能分类明确的接口要灵活、易用。

  • 信息隐藏要做好,模块的边界(接口)要清晰,用户对模块的了解要尽可能少(最少知识原则)

  • 模块内部实现要尽量符合“迪米特法则”和“契约式设计”

  • 只和必要的模块产生依赖,模块之间绝不可出现循环依赖

  • 一组功能相关的模块可放在一起,组成功能包,这样便于修改和维护(共同关闭原则)

2.设计时,应权衡一下是否要考虑可扩展性(需求将来是否会变化,如果比较稳定则不用考虑,避免过度设计)

如何设计来保证模块/函数具备一定的可扩展性(要符合开闭原则)?

  • 找出可能的变化,合理抽象(封装变化),提炼接口, 针对接口编程(依赖稳定的抽象/接口), 不依赖具体的实现

  • 组合优先继承(模块之间的依赖优先考虑是否可采用组合,使用继承主要是以使用多态为目的[里氏替换原则/针对接口编程])

  • 模块之间要正交(模块间功能要尽可能不重叠)

  • 高层模块和低层模块之间要符合”依赖倒置原则”和“好莱坞原则”

高层模块提供低层模块所需的接口,高层模块实现时针对这些接口编程,不依赖低层的具体实现;

而低层模块需要实现这些接口,实际运行时以多态的形式为高层模块提供功能(好莱坞原则)。

3.层次是一种软件抽象,分层设计可提高软件的灵活性,很多设计手法,其本质都是在软件的结构中加入更多的层次。

如对文件操作进行封装,提供一个文件操作类来代理文件操作请求,这样可屏蔽不同操作系统下文件操作API的不同,增加可移植性。

但也要充分考虑到层次带来的复杂性,会增加对软件理解和维护的难度。

因此,要努力控制层次在三层左右,否则宁愿牺牲一点抽象(软件设计是一个权衡和选择的过程)。

另外,层次抽象要保持内在的一致性,一个层次内部应该只做自己应该做的事,而不要去做其他层应该做的事。

如在设计DirectShow Filter时,应尽量把功能层次的模块与Filter层次分开,在Filter层次中只做DirectShow框架下filter应实现的功能,

如媒体类型的协商、与其前端/后端filter的连接、Filter之间的数据流传递等,而不涉及具体功能逻辑部分的实现;

同样,功能实现层也不应该依赖DirectShow框架(如数据的输出可用回调来实现解耦)。

这样做的好处是,以后可单独提取出具体的功能模块,移植到其他非DirectShow框架中,即Filter层仅是一层壳而已。

否则,这样的剥离将难以实现,同时也会因为层次边界的模糊而影响程序的可读性。

4.代码实现时,要有良好的编码风格,且风格要统一,函数和变量命名要准确表达其意;

逻辑实现要简洁利索,避免功能大而全的复杂函数,函数实现也要尽量符合KISS原则,只负责一件事;

总之,要时刻考虑软件维护者的感受(说不定几个月、几年之后你就是软件的维护者呢),尽可能减少让人产生怪异的感觉。

即尽一切方法提供程序的可读性。


附上一份关于软件设计基本原则的思维导图,以便参考。

软件设计基本指导思想

图片比较大,请移步这里直接查看原始文件。

(完)

Comments