设计模式之-单一职责&文件结构

听了公司架构师的软件设计分享,以及最近对整体的代码结构设计与优化的一系列事件,赶紧动动笔头,记下来。本文章主要记录的分享内容以及我后面查阅的资料以及一些自己的理解。

Bad Smell - Rigidity(僵化性)& Fragility(脆弱性)

软件设计中,往往会存在着各种各样的bad smell,本次主要以其中亮点进行讨论

1.Rigidity(僵化性)

僵化性是指难以对软件进行改动,即使是简单的改动。如果单一的改动会导致有依赖关系的模块中的连锁改动,那么设计就是僵化的。必须要改动的模块越多,设计就越僵化。
大部分开发人员都会以这样或那样的方式遇到过这种情况。他们会被要求做一个看起来简单的改动。他们仔细检查这个改动并对所需的工作做出了一个估算。但是过了一会儿,当他们实际进行改动时,会发现许多改动带来的影响自己并没有预测到。他们发现自己要在庞大的代码中搜寻这个变动,要改的模块数要远远超出最初估算,并且不断发现其他一些必须要记得做的更改。最后,改动所花费的时间比初始估算要长。当问他们为何估算得如此不准确时,他们会重复开发人员惯用的悲叹,“它比我想象的要复杂得多!”

2.Fragility(脆弱性)

脆弱性是指,在进行一个改动时,可能会导致程序的许多地方出现问题。常常是,出现新问题的地方与改动的地方并没有概念上的关联。要修正这些问题又会引出更多的问题,从而开发团队就像一只不停追逐自己尾巴的狗一样忙得团团转。

随着模块脆弱性增加,改动会引出意想不到的问题的可能性就越来越大。这看起来很荒谬,但是这样的模块非常常见。这些模块需要不断地修补——它们从来不会从错误列表中去掉。开发人员知道需要对它进行重新设计,但是谁都不愿意去面对重新设计中的难以琢磨性,你越是修改它们,它们举变得越糟。

以上两种特性,在我们日常开发的过程中,尤其困扰我。习惯于书写“面条式”代码的我们往往会被不断变化的需求与功能逻辑所困扰住,每一次变动都会使我们的代码变得越来越复杂与难看,臭味性越强烈。虽然伴随着模块化与组件化的引入,我们已经渐渐的开始进行模块拆分与功能复用。但是如何对其进行更加良好的组织,掌握好拆分的度,是我们本次讨论的重点。

单一职责原则(Single Responsibility Principle)

1.定义

There should never be more than one reason for a class to change,应该有且仅有一个原因引起类的变更。

当我们去对一个方法进行修改拥有两个动机的时候,也就意味着这个方法具有两个职责。而一个方法承担了过多的原则,在需求的变迁中,需要对其进行改写的可能性就更大。

这种方法对于修改代码来说,无疑是一件危险的事情,尤其是两个职责耦合在一起的时候,一个变化往往会引起一系列的修改,造成无法预测的破坏。

因此,SRP原则体现为: 一个对象(方法)只做一件事情

2.案例

automobile

对于一个复杂的Automobile类进行分析我们可以发现,很多方法并不是其应该具备的职责。

  • drive方法应是司机的职责,而不是automobile本身
  • wash则对应的为洗车店的职责
  • 修车行理应对与换轮胎和机油的职责

经过分析和拆分。 Automobile只剩下了其单一职责,整体智能更加清晰。当我们对其进行维护和修改的时候,只需针对功能职责,选择不容的类进行维护即可,大大的降低了维护成本。

automibile

如图所示的employee实例则能更好的表示出针对模糊对象方法的分析,对于Employee来说,计税、绩效虽然与其相关,但是真实来说不属于其自身职责,而是HR和财务部的事情。将不同的职责进行划分,更有利于其书写及扩展。

3.原则

并不是所有职责都应该同时分离

  • 倘若随着需求变化,两个职责总是同时变化的,那就不必对其进行分离。
  • 倘若两个耦合的方法没有发生改变的征兆和可能,不必主动对其分离。

方便性和稳定性方面需要做适当的取舍

例如jQuery的attr方法,既负责赋值,也负责取值。 理论上说是违反SPR原则的,但是这种方式能够更好的让开发者进行使用。所有说,我们未必要一味的遵守这个原则。

组织目录结构

伴随着类与功能的划分,新的问题也会产生。类与功能的拆分,我们将文件划分的越来越多。而前端所需维护的静态资源本身就比较多,包含html、js、css、图片等等等等,如何维护资源与资源间的关系变得更加复杂。为了解决这些问题,诞生了gulp、webpack、fis等一些列打包与构建工具,让我们不用手动的对其进行管理。然而,在对其进行手动修改或者任务交接的时候,往往还会由于文件众多而无从下手,搞不清文件与文件间的复杂关系。

功能划分还是资源类型划分

功能划分 VS
类型划分

先看图2,由于前段静态资源类型多的特点,人们从一开始就养成的“结构、样式和行为分离”的理念,更多人习惯性的将各类静态资源习惯性的分为如图所示。然而当模块不断增加的时候,这种划分方式的弊端也逐渐暴露出来。同一类型下的资源文件不断的增多,而同一类型,不同层级模块的划分,文件与文件的关系很难通过目录结构找寻出来。甚至阅读代码都不知该从那个文件开始入手。 及其依赖于文件命名于文档。

归根结地,其有以下弊端:

  • 静态资源间关系难以维护
  • 难以进行扩展分离

针对上述所讲的单一职责原则,我们在针对某一项目进行上手或者分析的时候,往往是通过“功能”来开始入手的。假设我们像图1一样,将个模块供能的静态资源都维护在一起,将模块与模块间的关系通过文件结构进行表示。 在对项目进行维护及接受的时候,我们只要搞清项目的功能结构即可对单个模块进行修改,岂不是很方便?

实践

针对此类划分方式,本人也相应的对其进行了实践,如图所示,针对某一功能模块,将其个功能进行单一功能划分,每个组件只保持其自身的功能,通过目录结构来表示组件与组件间的层级关系,各组件相应的静态资源由组件进行统一维护。 这样我们在开发的时候,关注点只统一在模块内部即可。

practice

注意

在实践过程中,往往会存在如何对于组件间通用资源维护的问题,因此,对于统一资源的维护与规划,是我们应注意的

Vue.js

针对vue.js 目前已形成了一套实践方案,在此可进行参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
+ common //共用文件
| + js
| | api.js //请求相关
| | base.js //常用公有函数
| | filter.js //常用filter
| + scss
| | + core //核心sass库,包括mixin等等
| | | index.scss
| | | reset.scss
| | | utils.scss
| | - theme //主题样式,包含变量等
| | index.scss //共用样式
| + img
+ components //通用组件,对 通用的组件才放这儿
| + foo //各个组件
| | -img
| | index.vue
| - bar
+ store //vuex对应的model,actions等
+ router.js //路由
+ views //根据功能划分的模块或页面
| + page1
| | + model1
| | | + child
| | | | index.vue
| | | | - img
| | | index.vue
| | - model2
| | index.vue
| - page2
main.js //主入口文件
App.vue //app文件
index.ejs //模板文件

参考