如何编写高质量的C#代码(一)

从”整洁代码“谈起

一千个读者,就有一千个哈姆雷特,代码质量也一样如此。前端

想必每个对于代码有追求的开发者,对于“高质量”这个词,或多或少都有本身的一丝理解。当我在长沙.NET技术社区群抛出这个问题时,众说纷纭。有人说注释齐全、可读性高,就是高质量;有人说变量命名、代码层次清晰,就说高质量的代码;有人说那些使用了新特性的代码,不少都是高质量代码;也有人说,高质量的代码是个伪命题,由于他每每要花大量的精力才能精心打磨,有这个时间,产品早就黄了。vue

说到”高质量“代码,就不得不提”整洁代码”。这个概念来源于畅销书《代码整洁之道》(The Clean Code)中,鲍勃大叔引入了这个整洁代码的概念。程序员

他认为:算法

写整洁代码,须要遵循大量的小技巧,贯彻艰苦习得的‘整洁感’”,这种“代码感”就说关键所在。有些人生而有之。有的人费点劲才能获得。它不只让咱们看到代码的优劣,还予咱们以借戒规之力化优为列的攻略。vuex

缺少”代码感”的程序员,看混乱是混乱,无处着手,有“代码感”的程序员,能从混乱中看出其余的可能与变化。“代码感”帮助程序员选出最好的方案,并指导程序员指定修改行动计划,按图索骥。c#

编写整洁代码的程序员就像艺术家,他可以用一系列变化把一块白板变做由优雅代码构成的系统。后端

这本书值得摆在每一位程序员的案头。许多热衷于英文原做的读者都会说国人翻译的许多做品都失去了原做的韵味,但这本韩磊老师翻译这本中文版十几年过去了,印刷了许多版了,也能客观证实这本译做的价值。设计模式

也许初读这本书,许多做者提到的手法咱们没法短期内认真体会,但许多读过这本书都表示,许多想法在咱们写代码的时候忽然迸溅而出,使得思路可以更加通达,并达到一种“人码合一”的状态。安全

”代码感“

在咱们大部分开发者看来,咱们开发的代码,每每无需涉及过于复杂的业务逻辑或底层技术,只需简单的使用一些代码拼凑,便可按时完成咱们的任务,也就说所谓的”CRUD业务开发者“。架构

但业务系统自己也并不是全靠所谓的“无代码平台”或“代码生成器”可以自动开发完成,他依然须要开发者用心去设计其中的逻辑、变量、结构、流程,才能更好的运转,尤为是要想让应用系统可以保持长久的生命力,更须要咱们可以编写更高质量的代码。

在《代码整洁之道》中,做者将这种编写高质量代码的能力,称为“代码感”,这种感受有时须要灵光一现,有时又须要花费大量的精力才能完成。

就像在《灌篮高手》中,安西教练让你们培养球感:

两万个球?写两万个类/方法/代码行?确实是一种提升”代码感“的好方法。

但跟投球要掌握方法同样,简单的重复写两万行代码估计很难提升代码质量,依然须要大量刻意练习才能带来质量上的提高。

而如何编写高质量代码,在软件开发领域,也有一些前人总结出来的良好准则,人们将这些准则,总结为“设计原则”。除了设计原则外,还要许多良好的实践模式,人们将它们称为”设计模式“。设计原则就像是内功心法,设计模式,则像招数功夫。

也许咱们没法彻底遵循这些原则或模式,但可以灵活的运用,总能给代码质量带来提高。

何为高质量代码

我我的认为:高质量代码是可读性强、易于测试,它们可以恰如其份的表达业务的须要,并能根据业务须要易于修改的代码。 高质量的代码也许与技术架构、特定API、特定的语言没有太大关系,但高质量代码或许都具有一些类似的特色。

代码结构

结构是代码的核心,就像高楼的支架,为整个代码的完整运行奠基基础。好的代码必定结构清晰,让人易于理解,并能快速定位问题、解决问题。

有人说好文章的结构特色即是:” 凤头、猪肚、豹尾“, 文章的起头要奇句夺目,引人入胜,如同凤头同样俊美精采;文章的主体要言之有物,紧凑而有气势,如同猪肚同样充实丰满;文章的结尾要转出别意,宕开警策,如同豹尾同样雄劲潇洒。 代码也许无需追求达到这么高的境界,但遵循必定清晰的代码结构也能达到一样的效果。

结构按照我我的的理解,可能包括如下几种层面:一、项目文件夹命名;二、分层;三、模块命名;四、代码格式。

一、项目文件夹

对于复杂项目,打开文件夹和解决方案的第一眼,是清晰仍是紊乱,每每就是咱们对于项目的第一印象。许多资深研发工程师,都会倾向于用数字来对文件夹进行编号,例如对于复杂项目,咱们使用以下命名方式对定义解决方案文件夹,虽然不会花特别多的功夫,但会给开发过程带来许多便利。

固然,因为在Visual Studio中,项目文件夹自己属于sln解决方案文件中定义的层级结构,并不会在资源管理器文件夹中体现,因此有时还须要在资源管理器文件夹中也定义相似的层级结构。

01 基础服务
02 框架服务
03 应用服务
   01 工做流服务
   02 权限服务
   03 日志服务

二、分层

分层式架构你们都习觉得常,其中尤为以三层架构(用户表现层,业务逻辑层,数据访问层)已经深刻人心,成为许多.NET开发者的广泛承认,而领域驱动设计最多见的则是四层式领域驱动设计(用户界面层,应用层,领域层,基础设施层)。

分层式架构体现了”关注度分离“的原则,在进行软件开发过程当中,能够根据需求,找到对应的逻辑分层,进行代码实现;有时不一样逻辑分层的组件会以各自不一样的发展速度迭代以知足不一样的需求;在适当的状况下,还能采用分布式架构,让不一样层运行在不一样的基础设施中,期间经过rpc等方式保持通讯,给架构留下了足够的弹性空间。

设计分层式架构并不是越多越好,尽可能控制在三到四层就足够了,否则会陷入”千层饼“的陷阱,过多的分层和过少的分层,其实没有任何区别。

对于后端工程师来讲,理解分层式架构并不困难,难的是要识别哪里逻辑代码应该归属于哪一层;而许多对于方兴未艾的前端技术来讲,如何分层,却彷佛并非一件容易的事,因为前端业务要适应来自用户层面的无穷变化,很容易就陷入“意大利面”式的代码混乱中。vuex框架为前端开发者提供了一种良好的示例,有时无需深刻了解vuex的机制,只需"模仿"这种分层方法,就能写出更加易于维护的前端代码了。

三、模块(类库)

模块的设计和耦合性

在.NET开发中,模块有时是一个独立的项目,并以一个独立dll(类库)的形式进行分发。模块也是最为常见的一种代码实践,但在《领域驱动设计·软件核心复杂性应对之道》一书中,做者埃里克·埃文斯却指出模块的运用,引发了“认知过载”的问题:

认知负荷理论认为,在问题解决和学习过程当中的各类认知加工活动均须要消耗认知资源,若全部活动所需的资源总量超过个体拥有的资源总量,就会引发资源的分配不足,从而影响个体学习或问题解决的效率,这种问题就说“认知过载”。

这段理论确实有点拗口,对应到软件开发过程当中,用通俗的说法,就是这个包承载的知识量太大了,把本来能够分离到多个模块中的逻辑代码都囊括进来,使得其反而下降了开发的效率。

尤为是类库的定义,不一样的开发者有不一样的习惯,有时按技术来划分,有时又按业务场景来划分,有时分拆,有时组合,“千人千面”,不连贯的设计思想,和“能用就行”的想法混合在一块儿,很容易就形成了一锅粥的状况。

在.NET项目中,每用一个using,就引入了一种耦合,而使用了new方法,建立了一个对象的示例,又引入了一个对象的耦合。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using xxx.Core;
using xxx.Infrastructure.Extension;
using Google.Protobuf.Collections;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;

而设计优良的代码模块,则可让依赖尽量的减小。

模块其实也是实践“高内聚,低耦合”思想的主要阵地,若是业务相关性很高的对象被划分到不一样的模块中,每每会使得开发者很难理解它们在业务上的做用,也会致使模块间的耦合进一步提升。

所以,好的模块设计应该将那些具备紧密概念关系的模型元素集中在一块儿,并能描述该模型元素的职能,使之成为一个内聚的概念集合。

组件设计的原则

关于如何设计模块,在《敏捷软件开发 原则、模式与实践》一书中,做者引述了如下设计原则基于粒度这个角度为组件的内聚性进行描述:

重用-发布等价原则(REP)

重用的粒度就是发布的粒度。REP指出,一个组件的重用粒度能够和发布粒度同样大。咱们所重用的任何东西都必须被发布和跟踪。简单的编写一个类,而后声称它是可重用的作法是不现实的。只有在创建一个跟踪系统,为潜在的使用者提供所须要的变动通知、安全性以及支持后,重用才有可能。

共同重用原则(CRP)

一个组件中的全部类应该是共同重用的,若是重用了组件中的一个类,那么就要重用组件中的全部类。

共同封闭原则(CCP)

组件中的全部类对于同一种性质的变化应该是共同封闭的。一个变化如果对一个封闭的组件产生影响,则将对组件中全部的类产生影响,而对其余组件则不形成任何影响。

从稳定性的角度为组件的内聚性进行描述:

无环依赖原则:

在组件中的依赖关系图中,不容许存在环。

稳定抽象原则

朝着稳定的方向进行依赖。

设计不能是彻底静态的。要使设计可维护,某种程度的易变性是必要的。咱们经过遵循共同封闭原则来达到这个目标。使用这个原则,能够建立对某些变化类型敏感的组件。这些组件设计为可变的。咱们指望他们变化。

稳定抽象原则

组件的抽象程度应该与其稳定程度一致。

四、代码格式

类的基本结构

代码格式,就是一个C#代码文件的逻辑结构。写代码实际上是一件成本很低的事,但维护代码,倒是一件成本很高的事。开发一个功能,只需短短几十分钟时间,但若是咱们要去找出代码中存在的缺陷,却每每须要花费大量的时间。

这就客观上要求,咱们书写的代码应该尽可能方便阅读(可读性)、检索(快速找问题)、易于维护,而书写出“格式化”的代码,大概是咱们可以提升代码质量的第一步。

对于书写的代码,大部分都是从上往下阅读,在须要阅读的代码较多量时,每每会选择折叠到定义,这样就能一眼看出每一个方法的用途,要达到这个效果,就意味着咱们须要精心设计安排代码的垂直格式。有经验的开发者每每会按照这种结构。

私有字段:定义类内部的基本成员,高层次概念,常量,和引入的算法。

构造函数:定义类的建立过程。

公共方法:定义类为外部暴露的行为。

私有方法:定义类为内部提供的行为。

类的格式要求

在《代码整洁之道》这本书中,做者介绍了他对于代码的格式要求:

垂直格式

代码文件的长度控制在200-500行左右,且短文件一般比长文件易于理解。垂直阅读时,顶部是粗线条概述,隐藏了故事细节,而后再不断展开。

每行展现一个表达式或一个子句,尤为是C#的链式语法,尽可能一行代码就是一个方法。

entity.Property(e => e.Memo)
.HasMaxLength(500)
.IsUnicode(false)
.HasComment("备注");

每组代码行展现一个完整的思路,思路间用空白行隔开。垂直方向上,靠近的代码能够展现它们之间的紧密关系,可以让代码更好阅读。

变量声明应尽量靠近其使用位置,由于函数很短,本地变量应该在函数的顶部出现。一个函数调用了另一个函数,应该把它们放到一块儿,且调用者应该在被调用者上面。概念相关的代码应该放到一块儿,相关性越强,彼此之间的距离就该越短。

横向格式

横向首先表如今代码的宽度上,尽可能控制在一行代码不超过120个字符。

水平方向上,能够用空格字符把彼此紧密相关的变量或对象链接在一块儿,也能够用空格将相关性较弱的对象分割开。

注意水平缩进和左对齐,尤为是上面提到的链式语法,若是点号没对齐,简直让人难受。

entity.Property(e => e.UserId)
   .HasMaxLength(10)
  .IsUnicode(false)
.IsFixedLength();
小结

本文对如何编写高质量代码进行了一些简单的概述,介绍了代码的分层、组件(包)的设计、以及整洁代码中的一些开发实践,经过了解这些知识,可以让咱们逐渐造成本身对于代码的体会,并经过不断的练习,将可以提升咱们的代码能力。

固然,有时,写文档、适当的作一些软件工程设计,看起来与完成代码编写无关,但也一样是提升代码质量的一种手法,经过就像许多好文章每每会先搭框架,好代码也一样如此。

根据业务须要,画一波流程图、领域模型图、类图、时序图可以让咱们的思路提早沉淀,让咱们的开发过程更加流畅,更能开发出高质量的代码。

下一篇,将对规范命名、注释、设计向量、设计原则、设计模式进行一些讨论。

相关文章
相关标签/搜索