代码之丑 例子
1,命名例子
这段代码做了什么?
public void processChapter(long chapterId) { Chapter chapter = this.repository.findByChapterId(chapterId); if (chapter == null) { throw new IllegalArgumentException("Unknown chapter [" + chapterId + "]"); } chapter.setTranslationState(TranslationState.TRANSLATING); this.repository.save(chapter); }
根据章节id从数据库中读取章节,把章节的状态改为翻译中,再把章节写回数据库。
把一个章节的翻译状态改为翻译中。
问题:需要阅读这段代码的细节,才能知道这段代码是做什么的?
将章节的状态改为翻译中,叫处理章节。
将章节的状态改为翻译完成,是不是也叫处理章节?
修改章节内容也叫处理章节?
命名过于宽泛,没有错,但不精准。
命名首先要能够描述这段代码做的事情。changeChapterToTranslating
如果把细节平铺开来,那本质上和直接阅读细节差别不大。
一个好的名字应该描述意图,而非细节。
我们为什么把翻译状态改为翻译中,这一定是有原因的,也就是意图。具体到这里的业务,我们把翻译状态修改为翻译中,是因为我们在这里开启了一个翻译的过程。所以,这段函数应该命名startTranslation。
2,
if (user.isEditor()) { service.editChapter(chapterId, title, content, true); } else { service.editChapter(chapterId, title, content, false); }
if判断的是参数,而不是动作。
到
boolean approved = user.isEditor(); service.editChapter(chapterId, title, content, approved);
到
boolean approved = isApproved(user); service.editChapter(chapterId, title, content, approved); private boolean isApproved(final User user) { return user.isEditor(); }
3,代码一次加一点,逐渐变坏
if (code == 400 || code == 401) { // 做一些错误处理 }
到
if (code == 400 || code == 401 || code == 402) { // 做一些错误处理 }
到
if (code == 400 || code == 401 || code == 402 || ... || code == 500 || ... || ... || code == 10000 || ...) { // 做一些错误处理 }
任何代码都经不起这种无意识的累积,每个人都没做错,但最终的结果很糟糕。
让营地比你来时更干净。
4, 大类,职责不单一
用户类
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; private AuthorType authorType; private ReviewStatus authorReviewStatus; private EditorType editorType; ... }
用户Id, 姓名,昵称,邮箱,电话号码
作者类型,表示作者是签约作者还是普通作者。签约作者可以设置作品的付费信息,而普通作者不能。
authorReviewStatus作者审核状态,作者成为签约作者,需要有一个申请审核的过程,这个状态就是审核的的状态。
编辑类型,编辑可以是主编,也可以是小编,他们的权限不一样。
对普通用户,普通用户既不是作者,也不是编辑。作者和编辑相关的字段,对普通用户来说没有意义。
对作者,编辑相关的字段也没有意义,作者是不能成为编辑的。
对编辑,作者相关的字段没有意义,编辑也不会成为作者。
问题:只有一个用户类。
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; ... }
作者类,
public class Author { private long userId; private AuthorType authorType; private ReviewStatus authorReviewStatus; ... }
编辑类,
public class Editor { private long userId; private EditorType editorType; ... }
拆出Author和Editor两个类,把作者和编辑相关的字段分别移到了这两个类里面。
在这两个类里分别有一个userId字段,用以识别这个角色是和哪个用户相关。
5,大类,字段分组
有时候,我们会觉得有一些字段确实都属于某个类,结果就是,这个类还是很大。
比如拆完的新的User类。
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; ... }
这些字段应该都算用户信息的一部分。但是,即便是相比于原来的User类小了很多。这个类依然也不算是一个小类。
原因就是,这个类里面的字段并不属于同一种类型的信息。比如,userId,name,nickname 几项,算是用户的基本信息,
而email, phoneNumber这些属于用户的联系方式。
从需求上看,基本信息是那种一旦确定就不怎么会改变的内容,而联系方式则会根据实际情况调整。比如,绑定各种社交媒体的账号。
如果我们把这些信息都放到一个类里面,这个类的稳定程度就要差一些。
把User类的字段分组,把不同的信息放到不同的类里面。
public class User { private long userId; private String name; private String nickname; private Contact contact; ... }
联系方式类
public class Contact { private String email; private String phoneNumber; ... }
把email和PhoneNumber放进去,后面再有任何关于联系方式的调整就放这个类里。
6,基本类型偏执
public double getEpubPrice(final boolean highQuality, final int chapterSequence) { ... }
根据章节信息获取EPUB(一种电子书格式)的价格。
问题出在返回值的类型上,也就是价格的类型上。
我们在数据库中存储价格的时候,就是用一个浮点数,这里用double可以保证计算的精度,这样的设计有什么问题?
这是很多人使用基本类型(Primitive)作为变量类型思考的角度。
实际上,这种采用基本类型的设计缺少了一个模型。
虽然价格本身是用浮点数在存储,但是价格和浮点数本身并不是同一个概念,有着不同的行为需求。
比如,一般情况下,我们要求商品的价格是大于0的,但double类型本身是没有这种限制的。
用double类型限制。要在使用的地方都保证价格的正确性,价格效应到处都是。
if (price <= 0) { throw new IllegalArgumentException("Price should be positive"); }
补齐这里缺失的模型,我们可以引入Price类型。校验在初始化时进行:
class Price { private long price; public Price(final double price) { if (price <= 0) { throw new IllegalArgumentException("Price should be positive"); } this.price = price; } }
以对象取代基本类型
如果觉得本文对您有帮助~可以
微信支持一下: