写出好代码并不难

源码 2024-9-18 19:00:39 61 0 来自 中国
更多文章请拜见:我的blog
0. 配景

本文重要讨论的内容是代码层面的内容。之前将软件行业的认知分为七个条理,这里重要的内容会合在代码层面,会涉及一些步伐、软件条理中的内容。得当与刚入行的同砚阅读。为低级工程师快速的成为中级工程师提供一条蹊径。
本文从几个常见的题目入手开始讨论写代码过程中大概遇到的题目(第一节),再讨论编写代码中除了常见题目还应该思量哪些题目(第二节),讨论完遇到的题目以及要思量的题目之后开始开始讨论哪些方式可以办理这些题目(第三节),到末了以实践的角度落地这些办理题目的方法(第四节)。以深入浅出的方式说明写出好代码并不是那么的难。
1. 代码为什么越写越乱?

在新写的代码和老代码的改造/重构过程中,总会发现由于技能、业务将一个简朴的题目弄的越来越复杂。从而导致代码越来越丢脸,越来越不能被明白。那么有哪些缘故原由导致代码越来越紊乱?
1.1 结构化步伐设计的重要特点流程式代码

从学习过程来看,任何一个研发职员都是先从面向过程编程开始学习变成的。在学习的过程中一个最重要的过程就是使用顺序的方式将所要实现的内容实现了。
在软件工程中我们学习过软件设计中须要包罗模块/组件,但是许多软件从业者没有办法将这个概念落地到代码编写过程中。由于没有办法分别模块的界限、确定模块的意义、形貌清晰模块间关系,以是就不举行模块的拆分。许多从业者不能明白模块/组件到底应该落在软件认知7层模子中的那一层也是影响开辟职员不举行模块分别的缘故原由。
1.1.1 流程化的代码编写方式有题目吗?

经常遇到一个文件打天下(天主类)的情况,比方在mvc下的service是不是天主类,该做的不应做的都放在这个内里?
一个Method走天下,照旧比方在mvc中一个业务都直接写在service的一个方法中。一个方法一样寻常情况下都在200行以上,这种就很轻易形成铁桶一块的题目。根本没有办法修改这个方法,假如要新添功能就直接在方法中加一块代码。
1.2 MVC是全能的?

在WEB编程鼓起的年代,也陪同着MVC模式的鼓起。以是,各人在学习WEB编程时都在学习和使用MVC模式。而MVC的特点是它由Model,View,Controller构成。而在微服务期间View都由前端实现,后端根本不消费心这方面的内容。Controller中重要做转换,控制,安全。那么业务逻辑应该在那里完成呢?在Model中吗?这就是MVC没有办法办理的题目。
1.2.1 面向对象

在MVC中现实实现过程中,Model中由Entity、Service和Dao构成。Entity负责业务实体,Service负责业务处理惩罚,Dao负责恒久化。而Service中写的代码都是以面向过程的方式举行编写的。以是Service和Model的概念都是冲突的。Model代表的是业务模子,Service根本就没有办法代表业务模子。也没有办法说用Java写的代码就是面向对象的。
1.2.2 代码的界限在那里?

上面说到的一个文件打天下,一个方法打天下是很显着的代码的界限是业务流程。并不消受惊,由于许多同事嘴上说着做技能,着实写代码的时间都是写的业务流程代码。
这里只想说这种代码界限是错误的。缘故原由有这么几点:

  • 业务流程的公用性比力小,以是导致方法,文件的公用性更小。从而导致代码无法复用。
  • 业务流程没有拆分,无步调则代码不易读。造成很难维护。
1.2.3 分层关系

在MVC的长处中做了一项叫做开闭原则:对扩睁开放,对修改关闭。做了差异条理之间的封装,做了一层隔离。
1.3 软件复杂度的三个泉源:规模,结构与厘革

《解构范畴驱动设计》中张逸老师说到软件复杂度的泉源:规模,结构与厘革。这三项最终都会落在代码中,比方业务会不停的发展,不停的增长,以是代码的规模也会不停的增长。轻微上规模一点的业务体系,都会扳连到各种各样的实体,以及实体之间的关系导致结构的复杂度提升。业务是否会演进,业务演进就会动员代码的厘革
在代码条理中怎么应对这些内容呢?
1.4 总结

从结构化变成深入到软件的复杂度,一起上都是各种题目来影响代码的编写过程。思量每一个方向都有大概和其他方向的代码有质的区别,我们这里并不讨论这几个方向深入之后会产生什么样的代码,我们这里讨论公共的一些好代码的写法。
1.4.1 题目

各人都风俗了流程化的代码编写方式,并深受MVC之毒(着实并非MVC题目,而是不深入思考)。在这种条件下又有这么复杂的题目须要编写代码来办理。代码写的烂是不是变成了正常事。
1.4.2 办理题目


  • 分治头脑
    结构化步伐设计的重要特点是扬弃 goto 语句,采取“自顶向下、渐渐细化、模块化”的引导头脑。
结构化步伐设计本质上照旧一种面向过程的设计头脑,但通过“自顶向下、渐渐细化、 模块化”的方法,将软件的复杂度控制在一定范围内,从而从团体上低落了软件开辟的复杂度。--李运华《从零开始学架构》


  • 不停的实践设计模式
    学会写OO代码,并可以大概明白KISS,DRY,SOLID,最少知识、向稳固依靠原则。并可以大概在工作过程中不停的实践这些原则。
下面重要说明落地这两个方向会遇到怎样的题目?以及怎样办理这些题目?
2. 怎么做到高内聚低耦合?

当代软件的代码第一目的是可读性,其他的变乱可靠,性能,安全等都是可以通过其他的方式办理的。以是,在编写代码的过程中第一要务是让代码能被别人看懂。
如下图随心所欲的服务和有规则的服务,有很大的区别。区别就在于怎么举行分类整理?

1.png 下面渐渐深入讨论一下代码怎么做到高内聚低耦合。第一步讨论分别代码模块(函数)时大概遇到的题目,第二部讨论分别模块(函数)时是不是应该思量全部原则,末了以同一的方式举行解答并引申到下一节的结构化。
2.1 模块/组件的界说

在编写代码过程中并不是只写方法(函数)就可以,还须要举行文件分别,Package分别。这些在某种意义上就是差异条理的模块分别。但是分别过程中大概会遇到这段代码应该放在文件A中照旧文件B中的题目,这里会从差异的层面列出题目,让各人对这部分有更深入的思考。
2.1.1 界说


  • 模块的责任怎样确定
    负责模块中的事件吗?
    负责模块中的恒久化动作?
    模块中的业务怎么确定应该在模块中照旧在模块外?
  • 模块之间的关系是什么样的?
    假如两个模块有关系,他们是不是直接举行调用?
    模块是不是落在差异的条理中?条理之间的依靠关系是不是服务的关系?条理是洋葱架构条理照旧上下分层?
    使用函数是编程中的处理惩罚类通报照旧使用DI的方式举行依靠通报?
  • 模块中任何地方都可以调用其他的服务吗?
    对外能力(方法、功能)怎么确定?
    内部能力(方法、功能)可以被任何其他模块调用吗?
2.1.2 工程代码与算法代码的区别


  • 算法代码代表着一个功能
    全部的代码都须要写在一起,由于必须在当前位置调解指针位置,调解当前值内容等。
  • 工程代码是分步调的
    一个业务写一个流程就可以办理全部的业务题目照旧按照规则去完成高内聚低耦合?
    范畴?微服务?限界上下文?范畴?OO(面向对象)
    工程代码是给人看的,以是第一要务是让人能看得懂
2.2 原则

除了SOLID之外另有KISS,DRY,BASE,约定大于设置,奥卡姆剃刀等等原则。那么在那里使用这些原则,有没有反模式?
2.2.1 写代码的时间是面向复用编程,照旧面向业务编程?

许多代码编写的过程都是BA/PM输出需求,然后开辟举行代码编写。那么开辟顺着需求的思路举行实现过程中是不是就变成了按照业务举行编程,再团结之前的题目一个Method打天下。就变成了业务流程写在一个方法中。那么怎么面向复用编程?
2.2.2 最少知识

接口规则怎么影响高内聚与低耦合?数据库中的2NF(部分子函数依靠)是不是会影响接口的界说?
内部实现的内容不应该通过参数被袒暴露来?
2.2.3 单一职责

举一个简朴的例子:

  • 在业务的参数校验中能不能举行服务间调用完成业务?
  • 上面说到工程代码是分步调的,步调之间的责任是否界说清晰?
  • 在Controller的Method中举行业务编写是否符合?Controller中应该干什么?
2.2.4 圈复杂度

圈复杂度大说明步伐代码的判定逻辑复杂,大概质量低,且难于测试和维护。复杂度越高代表读懂代码越难,其他人读懂代码代表着是不是可以维护。不过圈复杂度只能代表代码条理的可读性,不能代表步伐条理的可明白性。

2.png 这里说明最简朴的低落代码圈复杂度的方法:不要if中嵌套if,不要循环中嵌套循环。下面办理方案中会有更加美满的办理办法。
2.3 拆分+明白办理题目

从原来的紊乱无序,到结构化界说。着实就是使用拆分+明白界限的方式举行办理。拆分和明白可以使用大规则:抽象、分解和知识来举行。最须要的就是以这种方式举行思考,将这种思考模式应用到软件的各个条理。
3.png 2.3.1 实践


  • 空行的意义
    一个方法一个函数中,空行的意义是隔开差异步调之间的内容。在一些不消拆的很开的代码块之间有须要说明他们是差异的意义的代码块之间用空行隔开。
  • 方法名与范畴下令之间的关系
    面向对象课程中教过方法就是对象的活动。那么活动是鸭子叫?照旧你打了鸭子一下鸭子追着你叫?以是这里应该是须要相识对象是须要处理惩罚什么样的动作,然后动作中须要有什么处理惩罚流程。
  • 方法的阶段性分别
    代码编写范式就是设计模式,但是设计模式中没有说明一个业务代码应该怎样拆分步调。这里给出一个作者以为通用的方法阶段拆分流程。

    • 准备参数
      将参数校验中须要的数据准备好。
    • 参数校验
      举行参数的校验动作,比方在修改动作中查找原对象是否存在,对象的字段是否符合业务意义。
    • 业务步调
      业务动作,业务动作大概是多个步调。比方在电商中购物车天生订单的业务步调获取商品信息,暂存商品信息,天生订单,天生订单项,通知店家等等。
    • 返回结果
      返回处理惩罚结果

3. 代码怎么写才华不乱?

上面提了那么多题目,这里就开始说明怎么办理这些题目。着实管理一件变乱很简朴就须要处理惩罚三件变乱即可:

  • 明白事件界限
    下面以分包模式的说明举行讨论。
  • 明白事件间的关系
    下面以金字塔原理的方式办理。
  • 明白事件演进的方向即可
    这个着实在代码这个层面上比力少,以是就不举行说明白。
3.1 分包模式

软件工程的发展过程着实就是不停的明白包(组件)的职责与分别方法的发展史。清晰架构是到如今为止作者看到最新的一代分包模式,如下图所示。
[图片上传失败...(image-6d64ae-1659096072391)]](https://upload-images.jianshu.io/upload_images/2454595-f05c26388a7d99b0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/740)
这里的内容不举行详细讨论,如有爱好可以自行检索。这里就说明通过将业务、业务与技能链接、技能之间通过模块界说以及依靠关系界说的方式拆分开。以范畴模子管理业务中的复杂度落在代码就可以包管范畴中的代码的单一与简朴,以各种组件将业务与技能关联、技能代码拆离到差异的组件中。
3.2 金字塔原理

4.png 团结清晰架构对于条理与模块的分别。

  • 结论先行
    应用层负责聚合范畴服务来完成应用的业务,在编写这层代码时可以以结论先行的方式举行。即应用层只举行团体业务流程的编排工作,详细的业务操纵在范畴服务中完成。
    如许根本上看了应用层的代码就可以知道这部分业务的团体业务流程是什么样的。
  • 中央头脑明白
    以范畴模子的方式举行中央头脑明白,每一个范畴模子都有它本身的要处理惩罚的业务,而且不扳连到其他的业务。以范畴模子能力袒露的方式控制了模块的界限。
  • 先全局后细节
    以差异的条理的职责与依靠关系来管理细节的递进关系。通过不停的细化,让天主类灭亡。
4. 构造模块的方式?

应对软件复杂度的方式最有名的就是DDD,但DDD并没有现实的代码编写方式的引导工作。而Cola是DDD的一种比力全面的代码落地框架。
充血模子的题目可以拜见DDD 中的几个困难题目。以是这里没有以充血模子举行。根据作者浮浅的明白对拆包举行了些许的厘革,如有任何题目可以联系作者。
.├── cola-archetype #cola核心部分│   ├── config #设置管理│   ├── common #公共部分│   ├── adapter #接入层│   │   ├── controller #http接口│   │   ├── rpc #rpc接口│   │   └── amqp #消息队列消息入口│   ├── connector #外部代用│   │   ├── sms #短信接口│   │   ├── email #短信接口│   │   ├── amqp #消息队列消息出口│   │   └── db #数据库│   ├── app #应用层│   │   ├── AAA应用能力 #负责CQRS和Event的事项处理惩罚,以及应用业务流程的团体控制。│   │   └── BBB应用能力 #负责CQRS和Event的事项处理惩罚,以及应用业务流程的团体控制。│   ├── domain-service #范畴服务层│   │   ├── XXX范畴服务 #负责一个范畴中的对外能力的袒露。│   │   └── YYY范畴服务 #负责一个范畴中的对外能力的袒露。│   └── domain #范畴层│       ├── XXX范畴 #XXX的范畴分包│       │    ├── entity #负责这个范畴中的实体界说。│       │   ├── event #负责这个范畴中的变乱界说。│       │   ├── handler #负责这个范畴关心的变乱的处理惩罚。│       │   ├── service #负责这个范畴中的能力的提供。│       │   └── XXXAggregateRoot[.java/.go/.python/...] #范畴聚合根,大概和范畴服务层有些冲突│       └── YYY范畴 #YYY的范畴分包└── cola-components #cola组件部分    ├── dto #dto    ├── exception #exception    ├── extension #extension    └── test #test5. 总结

《重构》中有非常美满的代码好的样子和坏味道的样子。本文重要讨论的是代码管理以及代码编写中的内容。总结就是代码须要以先全局后细节的方式举行编写。
6. 参考

清晰架构
金字塔工作法
重构
圈复杂度
DDD 中的几个困难题目
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-25 02:54, Processed in 1.137966 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表