Click the Extensions view icon on the Sidebar (Ctrl/⌘+Shift+X).
Configuring VS Code:
UML三巨头之一的Ivar Jacobson曾说“银弹不存在,我们需要的仅仅是明智的软件开发方法( smart software development ),软件必须从一个小的可运行的skinny system 开始,逐渐充实生长称为full-fledge 的成熟系统。”
我们就采用这个思路,从hello world 开始不断迭代调试使代码长的越来越像一个命令行的菜单小程序。写代码要小步快跑不断迭代,罗马不是一天建成的,不要期望一撮而就。
另外需要说明的是,做实际项目并不鼓励一开始就从头开始写代码,而是找已有的类似项目做对比分析,对开源代码做逆向工程和再工程,对项目有深刻理解的基础上,再考虑是从头构建还是维护一个已有的项目来达成目标。
一个项目代码的风格就如同一个人给人的印象,代码风格之所以那么重要,是因为它往往决定了代码是否规范、是否易于阅读。
代码虽然最终是要给机器看的,但毕竟还是面向程序猿们的编程,程序猿们是要陪伴整个项目开发过程的。在编写代码的过程中,尤其是在协作开发的过程中,如果对方的代码杂乱无章,读起来都费劲,更别说还需要在此基础上进一步开发,这对程序员来说是个巨大的挑战。
好的代码风格不仅易于代码的阅读和理解,还能在很大程度上减少一些不必要的语法错误,例如少了"}" ,如果在编码的时候严格遵循了花括号的对齐规则,那此类错误将容易被避免。
一是规范整洁。遵守常规语言规范,合理使用空格、空行、缩进、注释等;
二是逻辑清晰。没有代码冗余、重复,让人清晰明了的命名规则。做到逻辑清晰不仅要求程序员的编程能力,更重要的是提高设计能力,选用合适的设计模式、软件架构风格可以有效改善代码的逻辑结构,会让代码简洁清晰;
三是优雅。优雅的代码是设计的艺术,是编码的艺术,是编程的最高追求。
缩进:4个空格;
行宽:< 100个字符;
代码行内要适当多留空格,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前后应当加空格。
在一个函数体内,逻揖上密切相关的语句之间不加空行,逻辑上不相关的代码块之间要适当留有空行以示区隔;
在复杂的表达式中要用括号来清楚的表示逻辑优先级;
花括号:所有‘{’ 和‘}’ 应独占一行且成对对齐;
不要把多条语句和多个变量的定义放在同一行;
命名:合适的命名会大大增加代码的可读性;
通过控制结构简化代码
传统上由于CPU计算资源和存储资源较为昂贵,在编写代码时往往更多地考虑最大限度地高效利用计算机资源,因此在编写代码的习惯上追求性能优先的策略。但是随着计算资源的硬件成本逐步降低,尤其是云计算技术的发展计算资源的价格大幅度下降,性能优先的策略背后隐藏的代价逐步显露。
如果您觉得控制流程盘根错节、判定过程难以理解、或者无条件的分支难以消除,那么就该重新返回到设计了。重新检查设计,搞清楚您遇到的问题是设计中的固有问题,还是设计转化为代码的过程中引入的问题。
返回设计重新思考设计,使得设计结构和代码结构在逻辑上保持一致,而不是“头痛医头脚痛医脚”的方式对代码修修补补。不断重构代码是编写代码的基本方式。
模块化(Modularity)是在软件系统设计时保持系统内各部分相对独立,以便每一个部分可以被独立地进行设计和开发。这个做法背后的基本原理是关注点的分离(SoC, Separation of Concerns),
从而整个软件系统也更容易定位软件缺陷bug,因为每一个软件缺陷bug都局限在很少的一两个软件模块内。
整个系统的变更和维护也更容易,因为一个软件模块内的变更只影响很少的几个软件模块。
软件设计中的模块化程度便成为了软件设计有多好的一个重要指标,一般我们使用耦合度(Coupling)和内聚度(Cohesion)来衡量软件模块化的程度.
耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。
一般在软件设计中我们追求松散耦合。
内聚度是指一个软件模块内部各种元素之间互相依赖的紧密程度。
理想的内聚是功能内聚,也就是一个软件模块只做一件事,只完成一个主要功能点或者一个软件特性(Feather)。
命令行菜单在开源社区中常见的写法
将数据结构和它的操作与菜单业务处理进行分离处理,尽管还是在同一个源代码文件中,但是已经在逻辑上做了切分,可以认为有了初步的模块化。
进行了模块化设计之后我们往往将设计的模块与实现的源代码文件有个映射对应关系,因此我们需要将数据结构和它的操作独立放到单独的源代码文件中,这时就需要设计合适的接口,以便于模块之间互相调用。
Consumer Reuse:消费者重用是指软件开发者在项目中重用已有的一些软件模块代码,以加快项目工作进度。软件开发者在重用已有的软件模块代码时一般会重点考虑如下四个关键因素:
尽管已经做了初步的模块化设计,但是分离出来的数据结构和它的操作还有很多菜单业务上的痕迹,我们要求这一个软件模块只做一件事,也就是功能内聚,那就要让它做好链表数据结构和对链表的操作,不应该涉及菜单业务功能上的东西;同样我们希望这一个软件模块与其他软件模块之间松散耦合,就需要定义简洁、清晰、明确的接口。
这时进一步优化这个初步的模块化代码就需要设计合适的接口。定义接口看起来是个很专业的事情,其实在我们生活中无处不在,比如我们看的电视剧中“天王盖地虎,宝塔镇河妖”就是黑社会接头定义的接口,比如两个人对话交流沟通使用的就是汉语普通话或标准英语这么一个接口规范。
接口就是互相联系的双方共同遵守的一种协议规范,在我们软件系统内部一般的接口方式是通过定义一组API函数来约定软件模块之间的沟通方式。换句话说,接口具体定义了软件模块对系统的其他部分提供了怎样的服务,以及系统的其他部分如何访问所提供的服务。
在面向过程的编程中,接口一般定义了数据结构及操作这些数据结构的函数;而在面向对象的编程中,接口是对象对外开放(public)的一组属性和方法的集合。函数或方法具体包括名称、参数和返回值等。
接口规格是软件系统的开发者正确使用一个软件模块需要知道的所有信息,那么这个软件模块的接口规格定义就必须清晰明确地说明正确使用本软件模块的信息。一般来说,接口规格包含五个基本要素:
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
由一系列独立的微服务共同组成软件系统的一种架构模式;
每个微服务单独部署,跑在自己的进程中,也就是说每个微服务可以有一个自己独立的运行环境和软件堆栈;
每个微服务为独立的业务功能开发,一般每个微服务应分解到最小可变产品(MVP),达到功能内聚的理想状态。微服务一般通过RESTful API接口方式进行封装;
系统中的各微服务是分布式管理的,各微服务之间非常强调隔离性,互相之间无耦合或者极为松散的耦合,系统通过前端应用或API网关来聚合各微服务完成整体系统的业务功能。
微服务架构的基本概念可以简单概括为通过模块化的思想垂直划分业务功能,传统单体集中式架构和微服务架构如下图示意
REST即REpresentational State Transfer的缩写,可以翻译为”表现层状态转化”。有表现层就有背后的信息实体,信息实体就是URI代表的资源,也可以是一种服务,状态转化就是通过HTTP协议里定义的四个表示操作方式的动词:GET、POST、PUT、DELETE,分别对应四种基本操作:
GET用来获取资源;
POST用来新建资源(也可以用于更新资源);
PUT用来更新资源;
DELETE用来删除资源。
更细致地对耦合度进一步划分的话,耦合度依次递增可以分为无耦合、数据耦合、标记耦合、控制耦合、公共耦合和内容耦合。这些耦合度划分的依据就是接口的定义方式,我们接下来重点分析一下公共耦合、数据耦合和标记耦合。
参数化上下文
通过参数来传递上下文的信息,而不是隐含依赖上下文环境,因此我们可以重新定义sum函数的接口如下代码:
移除前置条件
参数化上下文之后,我们发现这个接口还是有很大的局限性,就是在调用这个接口时有个前提,就是你有三个数,不是两个数,也不是5个数。必须有三个数就是前置条件。将这个前置条件移除掉,那就是我们可以求任意个数的和。
这个接口显然更通用了,既参数化了上下文又移除了原来的只能三个数求和的约束,但是又增加了一个约束条件,就是len的数值不能超过numbers数组定义的长度,否则会产生越界。后置条件也较为复杂,可能是只对numbers数组前len个数求和,所以后置条件不仅是返回值,还隐含了这个返回值是numbers数组前len个数的和。
简化后置条件
如果编程语言支持直接获得数组的个数,或者通过分析数组数据智能得出数组的个数,我们可以进一步移除前置条件len与numbers数组长度之间的约束关系,这样后置条件变为numbers数组所有元素的和,更加简单清晰。
可重入(reentrant)函数可以由多于一个任务并发使用,而不必担心数据错误。相反,不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入函数要么使用局部变量,要么在使用全局变量时保护自己的数据。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
所有的函数是不是都是可重入函数
不同的可重入函数有没有可能同时进入临界区
设计方法论: 不断地重构
几个重要的设计指导原则: