-->

后端面试38讲_16_14_软件设计的单一职责原则为什么说一个类文件打开最好不要超过一屏

你好,我是李智慧。

我在英特尔工作的时候啊,曾经接手过一个大数据circle引擎的开发工作。

我接手的时候呢,这个项目已经完成了早期的技术验证和架构设计,能够处理较为简单的标准circle语句。

后续公司打算成立一个专门的语组,开发支持完整的标准circle语法的大数据引擎,然后进一步将这个产品商业化。

我接手以后,打开项目一看,吓出一身冷汗。

这个项目只有几个类组成,其中最大的一个类负责circle语法的处理有近万行代码,代码中充斥着大量的switch case if else代码。

而且方法之间互相调用各种全局变量传递。

只有输入测试circle语句的时候,在第八个状态下才能够理解每一行代码的意思。

而这样的代码有一万行,现在只实现了不到百分之十的circle语法特性。

如果将ccl cle的全部语法特性都实现,那么这个类类有多多杂逻辑,有多复杂,维后有多么困难,而且还要准备一个团队来合作开发。

想想看,好几个在这样一个大文件里提交代码,想想都算爽。

当时我就觉得我太难了。

软件设计有两个基本准则,低耦合和高内聚。

我在前面讲到过的设计原则和后面将要讲的设计模式,大多数呢都是关于如何进行低耦合设计的。

而内聚性这主要是研究组成一个模块或者类的内部元素的功能相关性。

设计类的时候啊,我们应该把强相关的元素放在一个类里,而弱相关的元素呢放在类的位变,保持类的高内聚性。

具体设计的时候应该遵循这样一个设计原则,一个类应该只有一个引起它变化的原因,这就是软件设计的单一职责原则。

如果一个类承担的职责太多,就等于把这些职责的耦合在一起,这些耦合会导致类很脆弱。

当变化发生的时候,会引起不必要的修改,导致出现bug职责太多,还会导致类的代码太多,一个类太大,它就很难保证满足开闭原则。

如果不得不打开类文件进行修改,大段大段的代码呈现在屏幕上,一不小心就会引起不必要的bug.所以关于编程有这样一个最佳实践,一个类文件打开后一好啊不要超过屏幕的一屏。

这样做的好处是,一方面,代码少、职责单一,可以更容易的进行复用和扩展,更符合开闭原则。

另一方面,阅读简单,维护方便。

如何判断一个类的职责是否单一,就是看这个类是否只有一个引起它变化的原因。

我在文稿中给了一个设计,这个设计中长方形类react angle有两个方法,最个是计图方法,job,一个是计算面积方法。

Error有两个应用需要依赖这个react件打一,一个是几何计算应用,一个是图形界面应用。

绘图的时候啊,程序需要计算面积,但是计算面积的时候呢,程序又不需要绘图。

而在计算机屏幕上,绘图又是一件非常麻烦的事。

所以react angle类需要依赖一个专门的GUA组件包,这样就会出现一个单嘎的情形。

当我需要开发一个几何计算应用程序的时候,我需要依赖rerect angle类。

而rerect angle类又依赖了GUA包,一个GU le包,可能有几十兆,甚至数百兆。

本来几何计算程序作为一个纯科学计算程序,主要是一些数学计算代码。

现在程序打包完,却不得不把一个不相关的GUA组件也打包进来。

本来程序可能只有几百k现在呀变成了几百兆rect. Angle类的设计就违反了单一职责原则。

Rect angle承担了两个职责,一个是几何形状的计算,一个是在屏幕上绘制图形。

也就是说reject angle类有两个引起它变化的原因。

这种不必要的耦合不仅会导致科学计算程序庞大,而且当图形界面程序不得不修改。

Rect angle类的时候还得重新编译几何计算应用程序比较好的设计,是将这两个职责分离开来,将rerect angle类拆分成两个类,将几何面积计算方法拆分到一个独立的类。

Geetric rerect angle这个类负责图形面积计算error reorked angle类只保留单一绘图职责。

John现在绘制长方形的时候,可以使用计算面积的方法,而几何计算应用程序则不需要依赖一个不相关的绘图方法和一大堆的GUA组件。

事实上,web应用技术的发展演化过程也是一个不断进行职责分离,实现单一职责原则的过程。

在十几年前,互联网应用早期的时候,业务简单技术落后通常是一个类负责处理。

一个用户请求。

以java为例,就是一个serve elt完成一个请求处理。

这种技术方案有一个比较大的问题是请求处理以及响应的全部操作呢都是在serve let里serve elt获取请求数据进行逻辑困算,访问数据库得到处理结果,根据处理结果构造返回的HTML,这些职责全部都在一个类里面完成,特别是输出HTML需要在serve. Let中中一行一输输HHMML符串,这就比较痛苦了。

一个HTML文件可能会很大,在代码中一点一点的拼字符串、编程困难,维护困难,总之就是各种困难。

于是后来就有了GSP.如果说svellet是在程序中输出HTML,那么GSP就是在HTML里面调用程序。

同样的,文章的这里又使用GSP开发web程序的例子,用户请求提交给GSP.而GSP会依赖业务模型进行逻辑处理,并将模型的处理结果包装在HTML里面构造成一个动态页面,返回给用户使用GSP技术比savellet更容易开发一点,至少就不用再痛苦的进行HTML字符串拼接了。

通常基于GSP开发的web程序,在职责上也会进行一些最基本的分离,构造页面的GSP和处理逻辑的业务模型分离。

但是,这种分离藕断丝连,GSP中依然存在大量的业务逻辑分离代码和HTML标签耦合在一起,职责分离的并不彻底,真正将试图和模型分离的呢是后来出现各种MVC框架,MVC框架,通过控制器将视图与模型彻底分离。

视图中只包含HTMI标签和模板引擎的占位服务,业务模型则专门负责进行业务处理。

正是这种分离,使得前后端开发成为两个不同的工种。

前端工程师只做视图模板开发。

前端工程师只做业务开发,彼此之间没有直接的依赖和耦合,各自独立开发,维护自己的代码。

有了MVC,就可以顺理成章的将复杂的业务模型进行分层了。

通过分层方式,将业务模型分为业务层、服务层、数据持久层,使各层职责进一步分离,更符合单一职责原则。

让我们回到文章的标题类个职责应该是单一的,也就是引起类变化的原因应该只有一个,这样的类代码通常也比较少。

在开发实践中,一个类文件在IDE中打开,最好不要超过一屏。

文章开头那个大数据sql引引擎的例子,节cirql语法处理的类的主要问题是太多功能职责被放在一个类里面呢。

我在研读的原型代码,并和开发原型的同事讨论呢,我在这个类的职责从两个维度进行切分。

一个维度呢是处理过程,整个处理过程可以分为语法定义、语法变形和语法生成三环环节。

个cc QL语语具需需要类则层的环节。

此外,我在第一模块的第六篇文章中讲到,每个circle语法在处理的时候都需要生成一个circl语法树,而树是由很多节点组成的。

从这个角度讲,每个语法树节点都应该有一个单一的职责处理来来处理。

我从这两个纬度将原来有着近万行代码的类进行了职责拆分,拆分出几百个类。

每个类的职责都比较单一,只负责一个语法树节点的一个处理过程。

很多小的类只有几行代码,打开以后,只在ADA中的一小部分,在显示器上一目了然。

阅读维护都很轻松,类之间没有耦合,而在运行期。

根据社口语法数,将这些代表语法树节点的类构造成一棵树,然后用设计模式中的组合模式进行遍历,即可。

后续参与进来。

开发的同时,只需要针对还不支持的sql语法功能点,开发相对应的语法转换器类,全是former和语法树生成器generator就可以了,不需要对原来的类再进行修改,甚至不需要调用原类的类。

程序运行期在语法处理的时候,后续对应的语法结构后给相关的类处理就好了。

构构后,然类的数量扩涨了几百倍,但是代码总行数却减少了很多。

你在软件开发中有哪些可以用单一职责原则进行改进的设计呢?欢迎你在评论区写下你的思考,也欢迎把这篇文章分享给你的朋友,或者同事一起交流一下。