最近学到的一些技巧

功能设计的技巧(方法),如何设计一个类,如何设计一个表。代码层面一些常见的优化。spring断言。一些工具。

 

一、功能设计的技巧(方法)

1.必须先理解需求

个人认为,设计一个功能,永远是从读懂需求开始的。如果编码时没有彻底理解需求,就会遇到很多不必要的麻烦(漏掉一些细节/写出来的东西和需求有出入),导致返工。

2.“自上而下”和“自下而上”

理解完需求后,可以开始设计功能。设计流程主要有“自上而下”/“自下而上”。

“自上而下”就是先设计好入口类(controller),往下设计调用的接口(service),写实现类(impl),设计返回的封装类(dtos),设计需要使用的实体类(pojo),在这个过程中搞清楚自己需要些什么信息,总结起来设计表结构,写获取信息的方法(dao)。当然,用一些类似generator的工具可以同时把设计表、实体类、dao的事情全干了。

“自下而上”就是先设计表结构,再把dao、pojo都弄好。之后设计service,写impl,设计dtos,最后补充controller,就把整个流程补充完整了。

各人有各人的习惯,个人认为这两种比较靠谱。

3.我的习惯

(1)设计入口方法

我一般会先设计入口方法(用spring boot来举例,就是设计controller中的入口方法)。设计入口方法有三个关键点:我要通过什么路径来调用这个方法(调用方法)、我要提供什么参数(提供参数)、我收到的返回值是什么(返回值)。

(2)设计接口

写完controller,就要设计service层的接口,供controller调用。在设计接口时,一定要注释每个接口方法,写清楚需要的参数类型、返回类型、这个方法的使用说明(比如适用于什么情况、参数的限制,等等),方便后人调用时,能直接看到接口方法的详细信息。

(3)测试驱动(见仁见智)

写完了接口,我们应该马上写实现类吗?如果项目要求写单元测试,最好还是用测试驱动开发(TDD)的模式,先把单元测试写了。

写单元测试的时候,可以把边界值、特殊值、特殊情况全都搞清楚,写逻辑的时候就能避开雷区。如果不要求写单元测试,我们还是要假设所有的特殊情况,并且记录下来(手写、txt都行,不要太相信自己的记性)

基本上,我接触过的所有程序员都是讨厌写单元测试的,理由也很简单:“我怎么可能写错代码?让我再写一堆代码验证1 + 1 = 2,不是浪费我时间吗?(更何况有测试同学把关)”

个人认为,程序员的工作就是交出健壮的代码。只要你能保证代码没问题,谁管你写不写单元测试呢?这里要强调一个很严肃的问题:测试也是人。我们写完代码肯定要自己先测,把你想到的情况都测了,才交给测试,帮你找剩下的疏漏的错误,切忌一写完简单测测就扔给测试(相当于把代码质量交给测试把关了),结果测试找出来一大堆显式错误,浪费大家时间。如果这种事经常发生,别人肯定会觉得你的工作态度有问题。

(4)实现类

接下来写实现类。实现类是由多个实现方法组成的,每个实现方法包括了多个模块,每个模块实现了一小部分的逻辑(功能),等待我们逐个实现。在实现整个方法之前,最好先写上注释,这个模块的作用是什么,这个过程有点像写伪代码。如果要写一个购买商品的方法,我可能会这样准备:

这些注释最好对照着单元测试的注意事项来写。写完注释之后,把这些逻辑编码“翻译”出来即可。

在编码的过程中,我们要有“组件化”开发的思想,每个模块都是一块组件。比如“扣款,入库”这一步操作,一看就是经常使用的方法,我们可以找找相似的代码,直接拿过来用即可。

如果没有相似的代码,就要单独写成一个组件,方便后人调用。

如果某个组件非常复杂(让整个方法臃肿)/重复出现(出现冗余),我们就要考虑把这个组件抽出来写成一个私有方法,优化代码结构。

(5)设计dto、pojo、dao

随着每个模块的编写,我们逐渐清楚了自己需要什么样的类。这时候,我们先设计展示用的dto(根据前端需要的东西,我们就可以设计dto),再根据dto和逻辑,设计pojo,最后根据pojo,设计dao。这就把整个代码逻辑补完了。

(个人理解,dto和pojo的区别是,dto是展示用的数据,是提供给前端的结果,是从pojo处理(封装)得来的。pojo是满足业务需求的基本数据,只是单纯的数据记录。如果我展示的东西就是一个pojo,那么这个pojo也可以是dto。顺带一提,什么domain、bean之类的东西,其实都是pojo(我不太确定,请指正))

(6)养成写完就review的好习惯

编码完成后,我们应该马上实现下一个功能吗?个人认为,最好先review一次,趁着思路最清楚的时候找bug/看看实现是否合理,可以显著提高代码质量。

二、如何设计一个类

我们如何按照面向对象的思想,设计一个类?个人认为,一个类包括属性和行为。

User.java

id,username,password就是这个类的属性,是这个类需要用到的基本数据,下面的get、set方法,是围绕着属性的操作,是类的行为,我们可以用这个类做一些什么操作。这个东西大家都应该懂。

那么问题来了,我现在想封装一个dto,需要写一个getDto方法,那么这个方法,应该写在Service层中,还是写在这个dto类中?

个人认为有两种解释:

  1. “获取dto”是一种服务,所以写在Service层中,dto对象本身只是作为一个“承载信息的载体”而已。
  2. “获取dto”是dto类提供的一个方法,是dto类的自发行为。因为getDto方法和dto的属性有关,所以应该写在dto类中。

反正都解释得通,所以没必要纠结太多,我一般是写在dto类中的:

HuoLangRewardDto.java

如何设计一个类,只要遵循属性+行为的规范,其他的就见仁见智了。不过一个项目中最好还是有一个统一的规范。

三、如何设计一个表

我以前经常是这么干的:拿到需求后,理解需求,根据需求大致设计一个表,通过工具拿到dao、pojo、mapper,写其他部分。

后来我发现,把设计表作为起手式,问题是比较大的。举几个例子:设计一个表总有疏漏,如果漏掉某个字段,就要更改表结构,上层的dap、pojo、mapper全都要改写,非常麻烦。写完逻辑发现表过度设计了,根本不需要这么多字段,又要更改表结构…

如果先设计接口、理清楚大概的逻辑,就会对整体的功能更为了解,设计表时越能够一不到位,省掉了很多修改的功夫。

至于设计表时的规范,什么表名大写开头、字段小写开头、boolean类型不用is开头,参考《阿里巴巴Java开发手册》就行了,创表之前可以先过一下。如果不怕麻烦,数据库的字段描述要写上说明。如果有功能上的变动,必须修改描述,避免误导他人(我经常懒得写,这是不对的)。

最后一点,我们无法预计某个功能在将来是否需要拓展,最好“按照感觉”留下一些空字段给后人使用,避免变动表结构。

四、代码层面一些常见的优化

包括但不限于:

  1. 简明易懂的命名方式,看到方法名就清楚大概作用。
  2. 分解过于臃肿的类,细分成更小的模块。
  3. 常用方法写成工具类,方便别人调用。
  4. 常用的常数写成静态属性,方便修改。
  5. 关键逻辑加上注释,说明工作原理,编码思路。
  6. 接口方法加上注释,调用时更加直观。
  7. 关键逻辑多加日志,日志尽可能详细易懂,能更快定位问题所在。
  8. 类开头加上作者、使用说明(使用了哪些表、怎么去修改,等等),方便后人工作。
  9. 拒绝硬编码。
  10. 善用断言。

可以参考:

我对于“合适的代码”的一些想法

五、spring断言

直接看代码:

这里的Assert.isTrue()就是spring提供的断言方法了。在这段代码中,prices.length >= 2这个条件必须得到满足,否则将会中断程序的启动, 并且打印出后面的描述。

经常有这样的场景:系统启动时,从数据库中读取配置。如果配置有误(为null之类的),那么系统的运行肯定会出问题,应该阻止其启动。这时候断言就能派上用场了。

六、一些工具

1.lombok

本质上是个辅助编码的工具。官网:https://projectlombok.org/

用这个东西,可以通过注解取代java类中冗长的get、set方法、toString方法、构造方法,通过“注解换方法”的方式让java类变得清爽。

如果需要使用lombok,将其jar包加入pom.xml文件即可:

<scope>provided</scope>时表示:该包只在编译和测试的时候使用。可以猜测lombok的原理:通过识别java类中的注释,编译时直接在class文件(字节码)中加上相应的方法。

那么问题来了:只有编译后,class文件中才会有get、set方法(等等),编码时的java文件中是没有的。我们写代码的时候岂不是无法调用了?

为了解决这个问题,需要在Eclipse装上lombok插件:

使用指令后就能打开lombok界面,选中Eclipse所在的路径安装插件,重启Eclipse即可。其他高级用法请自行掌握。

2.JsonView.exe

查看json的工具,解析json更方便,免得每次都上www.json.com去解析。

JsonView.exe就是个小程序,不是百度搜JsonView出来的那个火狐插件…我没找到这个东西的官网,不过百度之后也有很多结果,可以自行下载。

七、总结

小小的工作总结。

发表评论

电子邮件地址不会被公开。 必填项已用*标注