canvas教程

java 的clean code 技巧(2)

字号+ 作者:H5之家 来源:H5之家 2017-06-02 10:05 我要评论( )

简而言之,我比较推崇写代码的时候“广度优先”而不是“深度优先”,这和我读代码的方式是一致的。当然,这件事情跟个人的思维习惯有一定的关系,可能对抽象思维能力要求会更高一些。如果开始写代码的时候这些不够

简而言之,我比较推崇写代码的时候“广度优先”而不是“深度优先”,这和我读代码的方式是一致的。当然,这件事情跟个人的思维习惯有一定的关系,可能对抽象思维能力要求会更高一些。如果开始写代码的时候这些不够清晰,起码要通过不断地重构,使代码达到这样的成色。

清晰的命名

老生常谈的话题,这里不展开讲了,但是必须要mark一下。有的时候,我思考一个方法命名的时间,比写一段代码的时间还长。原因还是那个逻辑:每当你写出一个类似于"temp"、"a"、"b"这样变量的时候,后面每一个维护代码的人,都需要用几倍的精力才能理顺。

并且这也是代码自描述最重要的基础。

避免过长参数

如果一个方法的参数长度超过4个,就需要警惕了。一方面,没有人能够记得清楚这些函数的语义;另一方面,代码的可读性会很差;最后,如果参数非常多,意味着一定有很多参数,在很多场景下,是没有用的,我们只能构造默认值的方式来传递。

解决这个问题的方法很简单,一般情况下我们会构造paramObject。用一个struct或者一个class来承载数据,一般这种对象是value object,不可变对象。这样,能极大程度提高代码的可复用性和可读性。在必要的时候,提供合适的build方法,来简化上层代码的开发成本。

避免过长方法和类

一个类或者方法过长的时候,读者总是很崩溃的。简单地把方法、类和职责拆细,往往会有立竿见影的成效。以类为例,拆分的维度有很多,常见的是横向/纵向。例如,如果一个service,处理的是跟一个库表对象相关的所有逻辑,横向拆分就是根据业务,把建立/更新/修改/通知等逻辑拆到不同的类里去;而纵向拆分,指的是
把数据库操作/MQ操作/Cache操作/对象校验等,拆到不同的对象里去,让主流程尽量简单可控,让同一个类,表达尽量同一个维度的东西。

让相同长度的代码段表示相同粒度的逻辑

这里想表达的是,尽量多地去抽取private方法,让代码具有自描述的能力。举个简单的例子

public void doSomeThing(Map params1,Map params2){ Do1 do1 = getDo1(params1); Do2 do2 = new Do2(); do2.setA(params2.get("a")); do2.setB(params2.get("b")); do2.setC(params2.get("c")); mergeDO(do1,do2); } private void getDo1(Map params1); private void mergeDo(do1,do2){...};

类似这种代码,在业务代码中随处可见。获取do1是一个方法,merge是一个方法,但获取do2的代码却在主流程里写了。这种代码,流程越长,读起来越累。很多人读代码的逻辑,是“广度优先”的。先读懂主流程,再去看细节。类似这种代码,如果能够把构造do2的代码,提取一个private 方法,就会舒服很多。

面向对象设计技巧 贫血与领域驱动

不得不承认,Spring已经成为企业级Java开发的事实标准。而大部分公司采用的三层/四层贫血模型,已经让我们的编码习惯,变成了面向DAO而不是面向对象。

缺少了必要的模型抽象和设计环节,使得代码冗长,复用程度比较差。每次撸代码的时候,从mapper撸起,好像已经成为不成文的规范。

好处是上手简单,学习成本低。但是每次都不能重用,然后面对两三千行的类看着眼花的时候,我的心是很痛的。关于领域驱动的设计模式,本文不会展开去讲。回归面向对象,还是跟大家share一些比较好的code技巧,能够在一个通用的框架下,尽量好的写出漂亮可重用的code。

个人认为,一个好的系统,一定离不开一套好的模型定义。梳理清楚系统中的核心模型,清楚的定义每个方法的类归属,无论对于代码的可读性、可交流性,还是和产品的沟通,都是有莫大好处的。

为每个方法找到合适的类归属,数据和行为尽量要在一起

如果一个类的所有方法,都是在操作另一个类的对象。这时候就要仔细想一想类的设计是否合理了。理论上讲,面向对象的设计,主张数据和行为在一起。这样,对象之间的结构才是清晰的,也能减少很多不必要的参数传递。

不过这里面有一个要讨论的方法:service对象。如果操作一个对象数据的所有方法都建立在对象内部,可能使对象承载了很多并不属于它本身职能的方法。

例如,我定义一个类,叫做person,。这个类有很多行为,比如:吃饭、睡觉、上厕所、生孩子;也有很多字段,比如:姓名、年龄、性格。

很明显,字段从更大程度上来讲,是定义和描述我这个人的,但很多行为和我的字段并不相关。上厕所的时候是不会关心我是几岁的。如果把所有关于人的行为全部在person内部承载,这个类一定会膨胀的不行。

这时候就体现了service方法的价值,如果一个行为,无法明确属于哪个领域对象,牵强地融入领域对象里,会显得很不自然。这时候,无状态的service可以发挥出它的作用。但一定要把握好这个度,回归本质,我们要把属于每个模型的行为合理的去划定归属。

警惕static

static方法,本质上来讲是面向过程的,无法清晰地反馈对象之间的关系。虽然有一些代码实例(自己实现单例或者Spring托管等)的无状态方法可以用static来表示,但这种抽象是浅层次的。说白了,如果我们所有调用static的地方,都写上import static,那么所有的功能就由类自己在承载了。

让我画一个类图?尴尬了……画不出来。

而单例的膨胀,很大程度上也是贫血模型带来的副作用。如果对象本身有血有肉,就不需要这么多无状态方法。

static真正适用的场景:工具方法,而不是业务方法。

巧用method object

method object是大型重构的常用技巧。当一段逻辑特别复杂的代码,充斥着各种参数传递和是非因果判断的时候,我首先想到的重构手段是提取method object。所谓method object,是一个有数据有行为的对象。依赖的数据会成为这个对象的变量,所有的行为会成为这个对象的内部方法。利用成员变量代替参数传递,会让代码简洁清爽很多。并且,把一段过程式的代码转换成对象代码,为很多面向对象编程才可以使用的继承/封装/多态等提供了基础。

举个例子,上文引用的代码如果用method object表示大概会变成这样

class DoMerger{ map params1; map params2; Do1 do1; Do2 do2; public DoMerger(Map params1,Map params2){ this.params1 = params1; this.params2 = parmas2; } public void invoke(){ do1 = getDo1(); do2 = getDo2(); mergeDO(do1,do2); } private Do1 getDo1(); private Do2 getDo2(); private void mergeDo(){ print(do1+do2); } } 面向接口编程

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • javascript教程java教程html教程css教程it之家

    javascript教程java教程html教程css教程it之家

    2017-06-01 10:00

  • JavaScript通过Html5 Canvas进行图像处理的方法

    JavaScript通过Html5 Canvas进行图像处理的方法

    2017-05-29 08:05

  • Android的动态壁纸视差滚动效果

    Android的动态壁纸视差滚动效果

    2017-05-26 17:03

  • 【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用

    【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用

    2017-05-26 08:00

网友点评
/