所得税怎么扣
个人所得税怎样缴纳个人所得税的征收方式可分为按月计征和按年计征。个体工商户的生产、经营所得,对企业事业单位的承包经营、承租经营所得,特定行业的工资、薪金所得,从中国境外取得的所得,实行按年计征应纳税额
2025.02.01在探讨具体的方法之前,首先要简述下,认知负载是如何影响工作效率的。认知负载理论中最重要的概念当属大脑的工作记忆(如下图所示),这是用来处理各种运算和逻辑推理的地方,有点类似计算机的内存,用于临时存储一些概念。相比于长时记忆,工作记忆空间极为有限,也是我们学习和工作的瓶颈所在。
图1 认知负载和工作记忆 (图片来自网络)
一般而言,工作记忆能够同时处理的概念数量是7±2(也就是5到9这个范围),而且,这是很难改变的。所以,不管是小学生,还是科学家,心算2位数的乘法对他们而言,难度应该是差不多的。另外,我还发现,如果几个概念互相之间具有较强的关联,会更容易被记住。因此,软件开发中,一个非常重要的原则,就是尽可能减少同一时刻大脑需要处理的概念数量,尤其是那些不相关概念的数量。这些概念包括但不限于:变量名、方法名、方法参数、返回值、条件判断、类型信息等等。
另外,当前要解决的问题(比如这个方法的返回值为什么不符合预期)或者要实现的目标(比如实现某个特定的业务功能),也都会占据工作记忆的空间,因此,留给其他概念的空间就更少了。
如果同一时刻要处理的概念太多,大脑就会超载,之前记住的一些概念就会被清除出去(有点类似于缓存的清除策略)。表现出来的现象就是,看代码的时候,经常看到后面忘了前面,需要倒回去再来一遍。甚至有时候需要反复看多遍,直到将这段逻辑载入长期记忆(有点类似交换空间)。可想而知,这种情况下,对效率的影响有多大。
软件开发中,在宏观的架构层面,可以使用关注点分离的思想,让我们某个时刻只需要关注某个较小的模块,同时尽可能减少与其他模块的关联;除此之外,在具体方法和代码层面,也有一些值得注意的小技巧,能够显著提高代码的可理解性和可维护性。
起个好名字
好的名字,能够清晰地表达概念的含义和作用,让我们在读代码的时候不需要花费额外的认知能力去思考,从而降低了认知负载。好的命名需要具备相关性、精确性和区分性。
相关性,指的是名字需要与其所表示的概念强相关,并且是在当前的上下文中相关。对于表示业务概念的变量名和方法名,不要自己随意命名,而是要尽量用专门的领域相关词汇(有些研发同学对于自己不了解的领域概念,可能直接用机器翻译出来的单词,很容易失去相关性),对于技术相关的名字,最好能够体现出背后的设计决策和模式(xxxFactory、xxxPipeline等),那些不具备相关性的名字,很容易被逐出工作记忆,从而影响对代码的理解。
精确性,指的是名字所指的概念和范畴最小化,没有隐藏含义,不需要二次思考。我曾经在某家公司负责交易系统,我们上游的促销系统中,有个用来表示优惠活动适用对象(比如,是针对商品还是运费)的字段,叫bizType,让人云里雾里,不知道其前缀biz所指为何物。在业务系统中,凡事皆可叫biz,加不加这个前缀,几乎没有区别,因此,这个名字的含义太宽泛了,应该缩小范畴(比如改成targetType,就是一个更好的选择)。太宽泛的命名,需要额外的思考,而额外的思考也会占据宝贵的工作记忆,大大降低效率。
区分性,指的是多个名字之间不能引起混淆,尤其当它们同时出现的时候。还拿上面促销系统举例,就在bizType所在的同一个类中,还有一个字段叫promotionType,表示优惠的计算方式(比如,是打折还是直减等),这两个字段名的区分性就不好。还有个更加极端的例子,在我们交易系统中,用来表示商品优惠金额和优惠后的价格这两个概念的字段,分别叫skuPromotionAmout和skuAmountPromotion,是不是这么对应的可能我记错了,因为当时我就记不住。这样的命名,不仅需要额外的思考,还会把名字与之接近的概念一股脑儿全引入工作记忆,让大脑不堪重负。
延迟加载概念
变量声明或定义,应该尽可能靠近它被使用到的地方,这样既可以推迟概念被加载到工作记忆中的时机,也能够减少同一时刻驻留在工作记忆中的概念数量,相当于给大脑减压。另外变量定义和使用放在一起,也能够让这两处代码形成一个完整的概念,从而进一步降低认知负载。其实对于计算机内存来说,也是类似,推迟定义变量的时机,也是一种GC友好的编码方式。最糟糕的定义变量的方式,莫过于在函数或方法最开始的地方,一股脑儿把需要用到的变量全部定义或声明出来,这种方式,一上来就把我们的工作记忆塞满,等到读到使用变量的地方,很有可能已经忘记了变量含义,又得回过头去温习,效率非常低下。看一个假想的例子,以下两种写法,运行结果完全相同,但是认知负载差异巨大:
图 2 通过安排变量位置降低认知负载
上图左边的写法,大脑里面同一时刻需要处理的概念数量最大可以达到10个(记住入参和这个方法的目的包含在内)并且定义的地方和使用的地方相隔较远,这意味着我们需要保持这种高强度的认知压力较长时间;而右边的写法,同一时刻需要处理的概念数量不超过5个,且需要时才加载进工作记忆,读起来就非常轻松。
如果业务逻辑确实比较复杂,必须要定义很多变量,还是有办法可以降低认知负载的,那就是把逻辑上相似或者互相关联的变量归为一组,放在一起,用空行隔开,以突出分组的用意。这样安排,是利用了邻近性法则,使得大脑自动把相似的物体归为一类,减少需要记住的概念数量,降低认知负载。
提前回收概念
很多方法在执行处理逻辑之前,都要先进行很多校验,比如判断入参是否合法,判断权限是否足够,判断当前资源的状态等等。一种写法是使用多层if嵌套——所有条件都满足后,在最内层的if语句块中写处理相关的代码,这就会形成“箭头型”代码。
图3 箭头型代码 (图片来源于网络)
这种写法的问题在于,每一层if判断都会形成认知负担,并且这个负担需要一直带下去 ——你得时刻记住,在哪些条件下才执行到当前位置。如果一些if条件本身很复杂,那情况会更糟,你甚至还没读到真正执行处理逻辑的位置,就精疲力尽了。
更好的方式,是使用“卫语句”,把嵌套的if语句写成多个平级的形式,每当一个if条件不满足的时候,就直接返回(或报异常)。这样做的好处,是每看完一个if语句块,你就可以安全地在思想上放下它(从工作记忆中移除),相当于做了一次“垃圾回收”,然后,就可以继续轻装上阵,更好地理解后面的逻辑。这样写出来的代码,虽然方法可能会长一些,但是理解起来更轻松。
图4 箭头型代码 vs 卫语句
上图中这个示例,我们假设if条件很简单,都只有一个变量,那么,左边的方式,当你看到内层执行处理的代码时,工作记忆里面此时需要记住的概念数量是6(算上方法名和参数名),而右边的方式,工作记忆中同一时刻的概念数量不超过3。实际中,差异可能会更大。
可能很多人都知道“箭头型”代码的问题及其优化方法,不过,只有深刻理解了背后认知方面的原理,才能够灵活运用这个思想,提前给读代码的人减负。比如,最小化变量作用域就是该思想的另一个很好的实践,我们应该尽可能把变量定义在使用它的最内层的块中(比如if、for、try块等)中, 另外,在很多语言中,甚至可以单独使用{}来显式定义一个块,块中的变量只在块中有效,块结束后,其中的变量也就不可见了。
图5 块作用域
上图示例代码中使用了显式的块作用域,块结束后,我们大可以放心地“忘掉”其内部出现过的变量a和b,因为我们知道后面再也不会用到了,这样就可以提前给大脑减负。(熟悉垃圾回收机制的同学可能也知道,这种写法还能够提示垃圾回收器,块内部的变量可以被提前回收掉了。)
最小化方法传参
上文提到过,读代码时,方法调用的参数也是需要载入工作记忆中的概念,因此,写代码时,如何传参,也很有讲究。
减少显式概念传递
方法参数是概念,方法传参本质上就是概念的传递,而概念是需要消耗认知资源的, 所以,应该尽可能减少方法参数。那些有着10个以上参数的方法,光是理解方法签名,就够我们喝一壶的了。因为难以理解,也就难以使用,还有可能被误用,尤其是在多个参数类型相同的情况下。
首先要避免的就是传递冗余参数。如果某个参数能够根据其他参数算出来(或者进行额外查询就能获得),就是冗余的,应该考虑去掉。(有时候,出于性能方面的考虑,可以做些妥协)。
在确实需要传递很多参数的业务场景下(通常是创建一些业务资源,比如用户、商品、订单等),应该把多个参数封装成对象(比如CreateOrderRequest)进行传递。很多时候,我们其实是在浏览代码,不需要搞清楚遇到的每个方法的细节,而仅仅是为了找到我们真正感兴趣的部分,所以这种封装参数的做法,能够显著地降低认知负载。
如果是对象的构造方法需要大量参数,可以考虑使用Builder模式,通过链式赋值,一次赋值一个字段,既增加了可读性,也能够避免出错。
减少隐式概念传递
对象参数,如果被不合理地使用,反而会增加认知负载。对象参数中的字段,是隐藏在对象后面的概念,如果我们点开这个对象,想看看方法调用的细节,这些概念也会被加载到工作记忆。有时候,某个方法实际只用到了入参对象中很少的几个字段,却要求传入整个对象,通常,这都是不合理的。
举个例子,假设在创建订单的场景中,需要调用一个计算运费的方法,该方法只需要用到用户地址id、商品总件数和商品总重量这三个字段,那么传递包含大量其他字段的CreateOrderRequest对象就不太合适,因为光看参数不看方法实现的话,你不知道计算运费需要用到哪些信息,很多原本不需要的字段,成了干扰和负担。另外,这种简单粗暴的传参方式,也会导致方法难以使用和测试——在拿不到CreateOrderRequest对象的场景下,直接实例化一个新对象,仅仅赋值需要的那几个字段后传下去,这既不方便,也埋下了坑。
避免不必要的函数调用和过深的嵌套
函数的最佳代码行数是多少,或者不应该超过多少行,这两个问题一直没有确切的答案。真相是,这本身是一个伪命题,真正的问题是,函数如何实现,认知负载最小。上文我们说过,有时候,长一些的函数,反而比短一些的函数更容易理解,只要它同时塞进我们工作记忆中的概念更少。
函数调用也是有开销的。对于计算机来说,得保留调用点的上下文,同时为被调函数创建新的上下文,函数返回后得恢复之前保留的上下文;对于人脑来说,也是类似的过程,进入新的函数后,在我们的工作记忆中也会发生上下文切换。如果被调用函数做了很多的事情,封装了足够的复杂性,调用这个函数就是值得的,因为总体而言,认知负载被降低了(所有概念不至于全部在一个函数里面,一股脑儿塞进工作记忆);相反,如果,被调函数仅仅只做了很简单的事情,或者相对实现代码来说,参数非常多,就有点得不偿失了。
如果实现逻辑不是很复杂,不断地调用嵌套的函数,一次只做一点事情,反而给读代码的人增加障碍,就像话不一次性说完,不断卖关子一样。
另外,有些时候,我们看代码,就是想搞清楚某个细节的来龙去脉,或者查清某个bug的根本原因,因此,需要不断跟到函数调用的深处。就像过深的调用堆栈会让程序报StackOverflow异常一样,过深的函数调用,也会让大脑认知超载。
限于篇幅,本文并没有面面俱到地覆盖所有我能想到的点,另外,写得太多,可能也会让大家认知超载,反而得不偿失。重要的是,希望越来越多的程序员们,能有这个意识,不仅要面向计算机优化自己的代码,更重要的,是面向我们的大脑优化代码,让代码更容易理解一些,造福自己,也造福他人。
更多精彩文章,欢迎关注微信公众号:技术凌云
个人所得税怎样缴纳个人所得税的征收方式可分为按月计征和按年计征。个体工商户的生产、经营所得,对企业事业单位的承包经营、承租经营所得,特定行业的工资、薪金所得,从中国境外取得的所得,实行按年计征应纳税额
2025.02.01今天想跟大家来用简短的时间来聊一聊企业所得税,企业所面临的税核心是两个,增值税和企业所得税,那么企业所得税怎么来理解他?从名字来看,当然它针对的是企业经营中的所得部分所征的税,这个很有趣啊,征值税针对
2025.02.05转让土地使用权差额所得税的标准是,能提供房屋原值的按照出售前后差额20%征收;未能提供房源原值的,统一按照出售总价的1%征收。只有二手房交易满五年,且是唯一住房的才能免征个税。【法律依据】根据《土地增
2025.02.05递延所得税负债的确认和计量一、递延所得税负债的确认1遵循原则①交易or事项发生时影响到会计利润or应纳税所得额的,所得税影响作为利润表中所得税费用的组成部分。②与直接计入所有者权益or事项相关,所得税
2025.02.06简单解析自然人代征自然人代征主要面对自由职业者,这个优惠政策很好地解决了一些无法按照规定有资格自行申请开具发票的纳税人因为其经营活动需要不得不提供发票的实际情况,企业在其经营的过程中,经常可能会碰巧遭
2025.02.06