看似简单但容易忽视的编程常识


这些年写了很多的代码、也读过很多的人写的代码,这几年,写代码的机会越来越少,但是每次写代码,感觉需要思考的东西越来越多,好的代码确实难能可贵,在国内业界中,好的软件不少,但是好的代码确实有点凤毛麟角了,写得出来的人不多,有追求的也不多,看到的好的代码越来越少。


可能是因为每个人对于好的评判标准不一,程序员中,也不乏文人相轻的较劲,总觉得比人写的代码都不够好,我不想介入这些无谓的争论,这篇文章中,我将结合我的编码经验,探讨一下,如何写出设计优良的代码,希望作为大家的参考。


好的代码首先是逻辑正确的



如何用编程语言表述正确的代码逻辑,这个问题好像很少有人单独拎出来讲,因为这个问题的答案很简单,简单得你都懒得去思考它,因为你肯定觉得,用编程语言正确的表述代码逻辑无非就是if 、while 之类的东西,有什么好探讨的,其实我要分享的并不是这些关键词的本身在逻辑中表达的含义,而是这些关键词的背后,编写程序的过程中,是否真的认真思考过背后的逻辑。


我曾不止一次遇到有多年编程经验的程序员,犯下类似的错误,也见过很多年轻的同学,反复强调纠正后,逻辑上还是会漏洞百出,这几年,我会经常组织我组里面的同学对代码进行走读,总结这些编码中的逻辑错误,很大一部分也是因为编程逻辑背后的思考是不够的。所以我要讲的,是很简单的知识,但是往往是最容易忽略的思考点。


我先给大家看一个例子:



这段代码为的目的是判断userInfo不为空串的时候couponing,看起来这段代码非常简单,判断上似乎还算比较严谨,其实这段代码只是看到了眼前要做的事情,但是并没有看到整体逻辑,为什么这么说呢,请看下面几行代码,也许会引发最这个简单问题新的思考。



这段代码虽说相比之前的代码长了一些,但是反映出来的是逻辑思考的严谨性,从这两个例子比较我们可以很明显的感觉到,第一段代码的问题,我们看到的只是为了保护是否能做couponing的条件,但是并没有去思考,条件不满足的时候,如何去做,是否有能力去恢复这个错误,确实无法恢复的时候,我们是否还要在错误的道路上越错越远呢,这一点非常重要,也很容易忽略,需要在编码的过程中,进行完整的思考才会意识到这个问题的,如果让错误继续执行下去,直到程序运行到下一个我们不期望的点,如果下一个不期望的点,代码上也遵循这个风格,简单的判断不为null,就跳过执行,这样下去,就会有无穷的隐患,代码整体上看上去,就漏洞百出了。所以从这里要给大家一个建议:


【要有一颗勇敢的心,程序不要害怕抛出错误,越害怕,错误越多】



我们应该都知道,错误越是早发现越好处理,其实程序在执行过程中也是一样的,越早发现错误,执行中就越容易处理。我一般称这种代码为代码的盲目容错,看上去这行代码很健壮,不会报错,但是不报错,不能影响错误的客观存在性,错会还是会存在的,遇到错误的时候,我们应该首先想到的是恢复这个错误,对容错问题,是需要进行非常深入很全局的思考才能做的决定,盲目的容错,只会让情况变得更加不可控制。



【千万不要忘记else的思考】


每当你要用到一个条件表达式的时候,切记要思考这个条件不成立的情况。 尽可能的不要出现只有if 没有else的情况,多组条件用 else if 连接使用,最后再加一个else去做大兜底。 其他的条件表达式类似,比如switch case 最后总有一个值得我们深思default。严谨的代码其实就提现在else上面的思考。



容易造成思考不足的条件语句


条件有两面性,思考要完整

有效降低逻辑的复杂度


上一节的例子中,肯定会有人觉得这样写代码,是不是觉得太复杂了,已经思考了这些问题,一定要用这么复杂的方式表达出来吗?这是另外一方面的问题,我们要让代码逻辑变得简单,这一节中,我尝试分享一些我如何降低代码复杂度的方法和经验。


还是用上面的例子,我尝试将代码变得更加简单,请看下面的代码,是不是感觉舒服很多。



这段代码中,表达了上面所有的逻辑,而且没有引入分支,其实这里我想强调的是


【减少分支就是降低复杂度】


我一般的编码思想是,尽可能的不要用分支处理异常,也不要因为异常引入分支,分支的使用场景最好是业务逻辑所需要的,应该用分支尽可能的表达清楚业务逻辑,而尽量不要用分支去适应异常的处理。这里进一步又引入了一个被忽略的尝试。


【不要混淆分支和异常的概念】



这一点看起来很难做到,但是根据我的实际经验,我们是有办法做到的,通过优雅的定义和处理异常,是可以比较容易的明确异常和业务分支的区别的。不过在本味中,我还是希望能将减少分之的方法说清楚,关于如何优雅的处理和定义异常,本文先不做过多描述。


我想说的是,一个分支,最好是能表达一层业务的含义,用分支标示是分支的条件以及条件成立或不成立的时候,要做的动作。所以,还是基于上面的例子,我们引入一个业务条件,“当用户是VIP用户的时候,我们才能给用户发放优惠券,否则,我们不发放优惠券”,我们分支代码标示如下:



这段代码正常的表述的业务的含义,注意其中的else,这里else 进入之后是直接return的,写上这一句就是上一节中,说明的一样,保证我们的代码逻辑是完整的,这一句有很明确的语义,就是表示条件不成立的时候,我们不做,如果不写的话,其实这部分语义是丢失的或是不明确的。


上面的代码能正常满足当前的业务需求,但是业务是复杂的,比如业务上我们有了新的需求,需要对发放优惠券的规则进行调整,调整会后的规则为,增加白名单可以不是VIP也要发优惠券,或者这个用户的用户UID是以00结尾,所以这时候,我们条件代码成了下面这个样子




这段代码中,我们逻辑一下就变得复杂了,虽说我们只用了一个if else 表达式,但是这里的分支复杂度其实是2的3次方,但是我们处理的情况就是两种,一种是成立,一种是不成立,所以,我们更加关心的是成立或是不成立的情况,而不是所有条件的组合形式,通过观察,我们发现,所有的逻辑都是由“或”进行连接,根据这个特性,其实我们可以提炼出逻辑工具方法,更好的表达我们更加关系的成立或不成立的条件。我们提取一个命名为any的逻辑方法来表述刚才的逻辑,这个方法接收一个不定长的参数,值要有一个为真,则返回为真。其他场景,我们也可以自己峰值其他的逻辑方法,比如all、notAll notAny。


则代码修改为



这段代码有效的减少了代码的分支数量,注意,这里仅仅是从分支数量上进行了减少,增加了一点点可读性,这样做的好处是,多数情况下,我们关注的业务分支的动作本身,而对于进入这个分组形成的的组合情况做所有讨论,所以,这样做,可以有效的降低分支的数量,减少用例的个数(写过单元测试的同学都知道,这样的逻辑要覆盖有多痛苦)。


这一节中,用了一个看上去有些鸡肋的方法去封装逻辑组合,其实,在现在日常生产中,想办法去封装逻辑表达式进行封装是非常有效果的,这里只是举了一个逻辑封装的例子,还有很多其它场景,比如从一个Map中,根据一组key逐个取值,如果取到值不为null,则放入到另外一个Map中,这里其实可以写一个putNotNull的方法来封装逻辑,这种做法非常有效。所以这一节我想给大家传递的一个思想,就是尽你最大的可能,对逻辑表达式进行封装


【减少分支数量就是减少复杂度】


代码和业务解耦


上一节的例子中,大家可以很容易看出来,不管逻辑怎么封装,代码是始终不稳定的,其实这里就引出了我们要强调的一个常识,就是能力要和业务解耦。

如何将能力和业务解耦,我对这个问题的理解是,首先我得把这个能力定义出来,这里我暂且定义为这个能力为发优惠券(其实定义一个能力是最难做的事情,深的思考,会发现这个问题难到需要重新思考人生,我这里不拉开篇幅讲了,结合这个例子,大家暂且先有一个模糊的理解,后面在慢慢讨论能力定义这个大的课题),有了这个能力定义之后,我们根据这个能力定义做一个面向能力的条件判断,代码示例如下:



从这几行代码中,可以看出,这里好像已经好了很多,我们将发优惠券的能力和判断条件canCouponing进行耦合,看上去这段代码已经稳定了,但是仔细观察后发现,canCouponing这个方法中依赖了userInfo,这个依赖貌似还是会存在很多问题,因为如果判断条件超出了userInfo的范畴,则这个地方又会变得难以解决,能力判断的要素看起来还是不可控的,为了解决这个问题,我们就要用到运行上下文或是领域模型的概念了,用一个运行时的上下文,作为数据信息载体,承载我们业务执行过程中所需要的模型数据,领域模型的发放则是我们对系统能力和业务有了足够深入理解之后,抽象出来的,能更加准确表述业务属性和行为的模型定义,在没有很好的理解和抽象之前,本节中我们还是先用运行上下文这样相对松散的概念来解决这个问题。根据这个思想,我们将代码进行修改:



在上面代码中,让runtimeContext中包含userInfo,通过一个更松散的对象来传递对象,交给canCouponing这个方法处理,这里也许有人会问,canCouponing这个方法内部还不是一堆逻辑,整体上还是控制不住复杂度。其实这类问题,我们将关键的业务点从硬代码中剥离出来,并且将业务逻辑集中起来进行管理的话,就可以使用规则引擎来处理了。通过规则引擎和专家系统,将这些规则交给业务人员或是运营人员统一进行管理就可以了,而我们的功能性代码可以做到非常的干净和稳定。


也许有另外的人会问,为什么couponing(userInfo,100);这行代码中没有用runtimeContext,而是直接使用的userInfo,在实际编程中,你可能真的需要用到runtimeContext,但是这里的目的是让大家理解如何让业务代码和能力解耦,关于能力本身这块如何更好的设计,这一方面的内容也有很多值得我们思考的,本文暂不做过多探讨。


【思考能力的定义,用代码描述能力,将业务从代码中抽出来,交给规则引擎或是专家系统处理】


来源:动力节点Java学院

文章版权归原作者所有,转载仅供学习使用,不用于任何商业用途,如有侵权请留言联系删除,感谢合作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET : Kerberos网络认证过程

今天抽时间初略学习了一下kerberos网络认证过程,作为笔记整理如下,希望与大家分享。 一、Kerberos初步定义: Kerberos这一名词来源于希腊神话“三个头的狗——地狱之门守护者”。Kerberos 是一种网络认证协议,其设计目标是通过密钥系统为客户…

MIPS投RISC-V是龙芯新征程的开始

日前,外媒报道MIPS Technologies宣布将放弃继续设计MIPS处理器,转向了RISC-V。在MIPS加盟RISC-V阵营后,有人鼓吹龙芯要完,但事实上,这完全是不了解龙芯具体情况的臆测。特别是在龙芯开发自主指令集LoongArch之后&#…

近期GitHub上最热门的开源项目(附链接)

2 月份 GitHub 上最热门的开源项目又出炉了,又有哪些新的项目挤进热门榜单了呢,一起来看看。1、nocodehttps://github.com/kelseyhightower/nocode Star 16256这是 2 月份新出炉的项目,可以说是 2018 年最火的佛系编程了,这个项目…

fb静态区域_fb 静态数据

在STAT中定义静态变量,并在INITIAL VALUE中设定初始值,静态变量的初始值会自动存如对应的背景数据块中回答者: 天晴09 - 初级工程师&nbsp&nbsp第9级2008-09-10 17:01:08你可以在fb中定义的时候直接输入初始值,也可以在ob…

9年没涨价,上太空……这些树莓派的冷知识你知道多少?

作为最成功的微型计算机,开源的树莓派(Raspberry Pi)在技术圈和学术界一直广受编程爱好者的好评,各路大神基于树莓派制作的新奇设备层出不穷,围绕这款微型计算机已经形成了一种独特的 DIY 文化,相关的开源软…

汇编语言入门教程

学习编程其实就是学高级语言,即那些为人类设计的计算机语言。但是,计算机不理解高级语言,必须通过编译器转成二进制代码,才能运行。学会高级语言,并不等于理解计算机实际的运行步骤。计算机真正能够理解的是低级语言&a…

轻量易用的微信Sdk发布——Magicodes.Wx.Sdk

概述最简洁最易于使用的微信Sdk,包括公众号Sdk、小程序Sdk、企业微信Sdk等,以及Abp VNext集成。名称NugetMagicodes.Wx.PublicAccount.SdkMagicodes.Wx.PublicAccount.Sdk.AspNetMagicodes.Wx.PublicAccount.Sdk.Abp如何贡献?如何快速封装一个…

数学界的高冷之王,N次拒绝巨额奖金:我穷,但是我不缺钱。。。

在现实生活中,你和谁在一起的确很重要,甚至能改变你的成长轨迹,决定你的人生成败。是否还记得,当你跟学霸做同学的时候,你总会莫名其妙跟他一起撸题目;当宿舍其他兄弟正在打游戏的时候,你也想着…

php-7.1.0,PHP 7.4.0 Alpha 1 v7.4.0 官方最新版

PHP团队近期宣布推出PHP 7.4.0首个版本PHP 7.4.0 Alpha 1,且下一个Alpha 2版本也在计划推出,不过作为早期测试版本,建议不要在生产环境中使用,想体验PHP最新运行逻辑的可以下载体验PHP 7.4.0 Alpha 1源码。基本简介PHP原始为Perso…

算法分析的正确姿势

一、前言在进一步学习数据结构与算法前,我们应该先掌握算法分析的一般方法。算法分析主要包括对算法的时空复杂度进行分析,但有些时候我们更关心算法的实际运行性能如何,此外,算法可视化是一项帮助我们理解算法实际执行过程的实用…

浏览器缓存机制的研究分享

源宝导读:互联网Web应用大行其道的今天,浏览器已经成为Web应用运行的重要平台。而Web应用对浏览器缓存机制的高效利用,可以大幅提升应用性能和用户体验。本文将对浏览器缓存机制进行系统化的梳理,分享我们的经验。一、背景计算机读…

Windows2008应用之配置客户端自动添加打印机

打印机对我们每一个人来说都是习以为常的东西了,给你一条远程打印机共享路径,你只要轻轻的双击想安装的打印机图标,等上个三五秒打印机就安装好,这台打印机就任你摆布了。但相对我们IT人员来说,全面的自动化将是我们的…

现代CSS进化史

英文:Peter Jang 编译:缪斯segmentfault.com/a/1190000013191860CSS一直被web开发者认为是最简单也是最难的一门奇葩语言。它的入门确实非常简单——你只需为元素定义好样式属性和值,看起来似乎需要做的工作也就这样嘛!然而在一些…

一日一技:Ocelot网关使用IdentityServer4认证

概述Ocelot是一个用.NET Core实现的开源API网关技术。IdentityServer4是一个基于OpenID Connect和OAuth2.0的针对ASP.NET Core的框架,以中间件的形式存在。OAuth是一种授权机制。系统产生一个短期的token,用来代替密码,供第三方应用使用。下面…

php windows共享内存,关于php的共享内存的使用和研究之由起

最近遇到一个场景,服务寻址的时候,需要请求远程的服务,获取一批可用的ip和端口地址及其权重。根据权重和随机算法选择最合适的一个服务地址,进行请求。由于服务地址在短时间之内不会发生变化,因此为了避免无限制的进行…

联想继续为其硬件产品完善Linux支持

喜欢就关注我们吧!此前,联想曾宣布为其台式机/笔记本电脑预装 Fedora/Ubuntu 等 Linux 发行版。并通过与 RedHat 等达成合作,为 Linux 带来了更多的上游工作支持。时至今日,据 Phoronix 称,自联想开始提供 Linux 预装以…

Excel有哪些需要熟练掌握而很多人不会的技能!

看完这篇Excel攻略,你会感觉这么多年的excel都白学了!来自知乎用户“未央之末”的分享。从今年年初的excel盲,到现在经常从大拿那偷师,也算是成长了不少,慢慢写下来算是对学习excel做个短期回顾——1排版篇给他人发送e…

如何在 ASP.NET Core 中使用 LazyCache

微软的 ASP.NET Core 已经是一个非常流行的用于构建 高性能, 模块化 并能运行在 Windows, Linux, MacOS 上的 WEB 框架,通常能够让程序保持高性能的一个有效途径就是通过缓存热链上的数据来应对高频的请求。LazyCache 是一款基于内存的易于使用和线程安全的缓存组件…

2018全球大学AI排名发布,中国高校表现强势!

[导读] 近日,麻省理工学院马萨诸塞校区计算机与信息科学学院教授 Emery Berger 发布一个全球院校计算机科学领域实力排名的开源项目 CSranking 更新了。目前,卡耐基梅隆大学(CMU)、麻省理工学院(MIT)与斯坦…

在C#中使用 CancellationToken 处理异步任务

在 .NET Core 中使用异步编程已经很普遍了, 你在项目中随处可见 async 和 await,它简化了异步操作,允许开发人员,使用同步的方式编写异步代码,你会发现在大部分的异步方法中,都提供了CancellationToken参数…