个人理解着重点
没有提及的内容多半是个人没有涉及到或一些大家都知道的且不需要进一步说明的规范
觉有理类别
包名统一使用单数形式
—— 编程规约 > 命名风格 > 9)
分包起名对类的分类有很重要的作用,想来生活中某个对象的类目是一个复数名词会很奇怪吧,例如将作者不称为“人”而称为“人们”。
杜绝完全不规范的缩写,避免望文不知义
—— 编程规约 > 命名风格 > 10)
“你以为的你以为就是我以为的我以为吗?”
定义DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值
—— 编程规约 > OOP规约 > 9)
手册反例:POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具 体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
个人理解:POJO中包含默认值相当于参与业务逻辑,不符合POJO本身简单类的定义,增大错误排查难度,与手册 编程规约 > OOP规约 > 17) 有相同表达目标
POJO类中布尔类型的属性都不要加
is
前缀,否则部分框架解析会导致序列化错误—— 编程规约 > 命名风格 > 8)
个人其实在定义POJO的boolean属性时挺喜欢使用is前缀,觉得更见名知意些,以表开关状态,且统一命名前缀添加is的话,在正常代码编撰时候直接’POJO.is’时候IDE就能过滤掉其他成员,减少检索所需成员的时间。
但细想来,当我表达一个状态属性的时候,例如’选择状态’,’选择状态’的值内容应该是由该状态声明的类型去控制,而在boolean属性的命名中添加is前缀似乎是想要在命名中限定(表达)该属性的类型,这就像一样物品的名字中包含物品的类目一样奇怪,例如 “碗” 被唤作 “器皿-碗”。鉴于这点,自己以后的表达例如’选择状态’属性命名就会偏向于由 ‘isSelected’ 调整为 ‘selectStatus’ 或 ‘selected’。
所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较
—— 编程规约 > OOP规约 > 7)
手册中该条说明举了一个Integer包装类==对比的例子,虽然着实知道Integer对象重用这回事,但是还真觉得写的时候可能会忘记 - -
当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读,此条规则优先于下一行规则。
类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法。
—— 编程规约 > OOP规约 > 14,15)
引用手册说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类 关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个 黑盒实现;因为承载的信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体 最后。
下列情形,需要进行参数校验: 1 ) 调用频次低的方法。 2 ) 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参 数错误导致中间执行回退,或者错误,那得不偿失。 3 ) 需要极高稳定性和可用性的方法。 4 ) 对外提供的开放接口,不管是 RPC / API / HTTP 接口。 5) 敏感权限入口。
下列情形,不需要进行参数校验: 1 ) 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。 2 ) 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底 层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一台服务器中,所 以 DAO 的参数校验,可以省略。 3 ) 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数。
—— 编程规约 > 控制语句 > 7,8)
正常编撰代码时候都会遇到参数校验的问题,调用方法之前要不要做一遍参数过滤校验?抽取了方法,方法体内要不要再做一遍参数校验云云,该条规约明确规定了哪种情况必须/需要/可以不/不用校验,且规定合理,代码编撰期间参数校验方面的情况可以加以参考手册规约。
共鸣类别
不允许任何魔法值(即未经定义的常量)直接出现在代码中
—— 编程规约 > 常量定义 > 1)
“又出问题了?呀,有个写死的常量值忘记改了!”,
“你这写的是啥,这个’1’是啥意思,这个’2’是啥意思,这个’666’又是啥意思?”
不要使用一个常量类维护所有常量,按常量功能进行归类分开维护
—— 编程规约 > 常量定义 > 3)
“你听我跟你说,这个前缀代表着这个领域的哪些值,这个前缀代表着那个领域的哪些值…#@$%@”,”我不听”
所有的POJO类属性必须使用包装数据类型,RPC方法的返回值和参数必须使用包装数据类型
—— 编程规约 > OOP规约 > 8)1,2)
曾经撰写代码的一次,我将DTO属性类型定义为基本类型byte, 我传1代表获取好看的妹子,传2代表获取身材好的妹子。写完之后总觉得哪里不对劲,对着代码发了半个小时的楞:“可是,如果我想不传的话,怎么办呢?”
还有一次跟后台交流,“我把类型定义为基本类型byte, 如果你这个值返回是1就代表这是个好看的妹子,2代表这是个身材好的妹子,如果你不传,我就默认它为-1,如果-1就代表你没反,你应该不会给我返回-1吧”,后台一脸懵逼
另外引用手册中一句话:“POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式的进行赋值,任何NPE问题,或者入库检查,都必须由使用者来保证”
类成员与方法访问控制从严
—— 编程规约 > OOP规约 > 20
- “对了,那个密码改了,你记录一下”
- “为啥改”
- “职位前任离职了,他知道密码”
- “这么不信任人家”
- “我要让前任别想从我这带走任何东西”
引用手册说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。 思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 service 方法,或者 一个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的 视线内,变量作用域太大,无限制的到处跑,那么你会担心的。
在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程 序将继续执行到哪一个 case 为止 ; 在一个 switch 块内,都必须包含一个 default 语句并且 放在最后,即使它什么代码也没有
—— 编程规约 > 控制语句 > 1)
与后台交流: “当你返个1的时候代表是个漂亮妹子,当你返个2的时候代表是个身材好妹子,可是,当你返回1或2之外的值的时候,代表什么呢?”
后台一脸懵逼: “what?”
在代码撰写过程中会很频繁的使用基本数据类型的值的不同来表达情况的不同,自然而然的使用方便的switch语句。于是每每写到类似代码时候,在最后加上一个default之后会看着代码发愣,“可是,如果以上条件都不成立,我应该做什么呢?”
当然具体做什么肯定是要根据业务代码上下文加以考虑,这条规范就是强制程序员去思考并处理其它状态下的情况以免将代码以不可预料的情况下往下流。就像规约中所说的,即使它什么代码也没有,但它明确表示了你对其他情况下的处理为什么都不做而不是可能忘记/忽略了。
表达异常的分支时,少用 if-else 方式
—— 编程规约 > 控制语句 > 3)
“如果这样,否则这样,再否则这样,再再否则这样,再再再否则这样…”
“你冷酷,你无情。你才冷酷你才无情。我那里冷酷哪里无情。你哪里都冷酷哪里都无情…”
谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。 说明:代码被注释掉有两种可能性:1 ) 后续会恢复此段代码逻辑。2 ) 永久不用。前者如果没 有备注信息,难以知晓注释动机。后者建议直接删掉 ( 代码仓库保存了历史代码 )
—— 编程规约 > 注释规约 > 8)
及时清理不再使用的代码段或配置信息。对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
—— 编程规约 > 其他 > 8)
在产品代码更新迭代过程中,难免会有一些旧代码不会再有用途,这时程序员有时会容易走一个极端,将旧代码直接随意注释,同时也拥有一个觉有理的理由:“万一以后用到了呢?”。以致于后来看代码,一个文件中能有1/3的注释,想来就算以后真用得着,检索起来和对目前代码的可读性的侵蚀都是个大麻烦,且随着产品的不断迭代,被注释的代码属于不被维护的代码,解除注释之后真的还会能用吗?
如果代码可预见今天明天用不到,那就清理后向版本控制提交一条修改记录,并给记录起一个易懂的标题,便于后期主动查找吧。
捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容
—— 异常日志 > 异常处理 4)
“这段代码为什么try,并且只打印了堆栈信息?”,“这里上次出了个异常,就try起来了”
每每与人交流写代码的感受的时候,都会说一句:“你要知道你在做什么”。
相关名词理解
POJO
POJO (Plain Old Java Object) , 这种叫法是Martin Fowler、Rebecca Parsons和Josh MacKenzie在2000年的一次演讲的时候提出来的。按照Martin Fowler的解释是“Plain Old Java Object”,从字面上翻译为“纯洁老式的java对象”,但大家都使用“简单java对象”来称呼它。
有一些private的参数作为对象的属性,然后针对每一个参数定义get和set方法访问的接口。没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的java对象
POJO格式用于数据的临时传递,它只能装载数据,作为数据存储的载体,不具有逻辑处理的能力
PO
Persistent Object 持久化对象,将物理数据的一种对象表示的承载,可以简化对象数据与物理数据之间的转换,不受任何业务的干涉
如果持久层是关系型数据库,那么数据表中的每个字段分别对应PO的属性
在阿里巴巴java开发手册中描述的DO(Data Object, DO还有其他含义,如: Domain Object)与PO相同概念,
DTO
Data Transfer Object 数据传输对象,数据传输内容的承载对象
VO
View Object 视图对象,可范理解为某个页面、视图组件数据的承载对象
BO
Business Object 业务对象, 主要作用是把业务逻辑封装为一个对象,对象中可以包含一个或多个其它对象
如一个简历中有教育经历,工作经历,社会关系等
Query
数据查询对象,各层接收上层的查询请求
手册中表示“超过两个参数的查询封装不能使用Map类来传输”, 对这条点赞,令人懵逼的 Map<String, String>
JavaBean
一种JAVA语言写成的可重用组件。JavaBean符合一定规范编写的Java类,不是一种技术,而是一种规范。大家针对这种规范,总结了很多开发技巧、工具函数。符合这种规范的类,可以被其它的程序员或者框架使用。它的方法命名,构造及行为必须符合特定的约定
- 所有属性为private
- 这个类必须有一个公共的缺省构造函数
- 这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范
- 这个类应是可序列化的。实现serializable接口
JavaBean中往往会封装一些简单逻辑,javabean中可以有其它的方法
DO
Domain Object 领域对象, 从现实世界中抽象出来的有形或无形的业务实体
特定领域的业务逻辑对象,可以拥有业务方法
如人类,存在嘴巴、手脚,拥有吃饭、跑步行为
引用 PO BO VO DTO POJO DAO DO这些Java中的概念分别指一些什么? - Knight王的回答
当你业务足够简单时,一个POJO 也完全当做PO BO DTO VO 看 比如有个用户类 只有 name 以及 phone 对于数据库层面也就两列,业务层面,传输,和前台展示时 都只有这两项
他们区别开来的例子: 1 、还是用户类 name phone 加了个password。 那么你后端的PO属性也是这3个,一般数据库里这个表有几个字段你的PO就有多少属性,但是传输到前台或者展现时,我们不应该把password 密码这种东西也一起传过去,所以他们的DTO VO 就还是 name + phone po : name phone password dto : name phone vo : name phone
2、现在又加了一个 枚举的状态位 status 表示用户的一些特殊状态,前台不会直接显示,可能会根据这个状态产生后续的操作 po : name phone password status dto : name phone status vo : name phone
3、BO ,一个用户下面 肯定会关联很多其他的表 比如用户设置 用户信息等,那么这个BO 下 不但有用户本身的一些属性,还包含了用户设置 和用户信息这两个类
依赖库
- 一方库 本工程子项目模块以来的库
- 二方库 公司内部发布到中央仓库,可供公司内部其他应用依赖的库
- 三方库 公司之外的依赖库
相关链接
- 阿里巴巴Java开发手册&IDE规约插件
- JAVABEAN EJB POJO区别
- 领域驱动设计系列文章(1)——通过现实例子显示领域驱动设计的威力
- 领域驱动设计系列文章(2)——浅析VO、DTO、DO、PO的概念、区别和用处