架构师技能1:Java工程规范、浅析领域模型VO、DTO、DO、PO、优秀命名

把我们之前项目的相关规范做了个总结记录下来,仅供参考,望能有点帮助。

每个人的代码风格迥异,通过统一的编码风格使得代码可读性大大提高。

编程规约或者编码规范的的本质是提高了代码的可读性,最终目的是提高团队协作效率,降低工程维护成本。

一、项目的应用分层:


代码分层,让不同层次的代码做不同的动作。层次清晰的代码,提高可读性,从代码结构就大概能了解到代码是如何分层,每层大概功能是什么。例如常用的Controller、Service、Mapper/Dao三层代码结构,其各层的代码逻辑范围。

默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web 层,也可以直接依赖于 Service 层,依此类推:

1、开放接口api层/controller: 可直接封装 Service 接口暴露成 RPC 接口; 通过 Web 封装成 http 接口; 网关控 制层等。

2、Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。

3、Service 层:相对具体的业务逻辑服务层。

4、Manager 层:通用业务处理层,它有如下特征

  • 1)封装下沉通用能力 对 Service 层通用能力的下沉,如缓存方案、 中间件通用处理;如mqMannager。
  • 2)封装第三方接口:对第三方平台封装的层,预处理返回结果及转化异常信息,如rpcMannager。
  • 3) 与 DAO 层交互,对 DAO 的业务通用能力的封装。

5、DAO 层:持久层,数据访问层,与底层 MySQL、 Oracle、 Hbase 进行数据交互,用来封装对数据库的访问(CRUD)

6、Model, 领域模型对象:   (第二节专门讨论) 

7、外部接口或第三方平台: 包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。

分层对于代码规范是比较重要,决定着以后的代码是否可复用,是否职责清晰,边界清晰。分层其实见仁见智,只要符合团队规范就可以。比如我们之前的团队工程规范:

api层/controller:轻业务逻辑,参数校验,异常兜底。通常这种接口可以轻易更换接口类型,所以业务逻辑必须要轻,甚至不做具体逻辑。该层的领域模型不允许传入DAO层。

Service:业务层,复用性较低,推荐每一个controller方法都得对应一个service,不要把业务编排放在controller中去做。允许操作DO数据领域模型Model。

在这一层设置事务:@Transactional(rollbackFor = Exception.class),防止事务混乱设置,导致一些不可控的事物异常。

Mannager:高可复用逻辑层。主要是做的是业务逻辑处理和数据组装。允许操作DO数据领域模型Model。在这一层是不允许设置事务,异常往外抛出,防止事务混乱设置,导致一些不可控的事物异常。

service层之间是否可以相互调用?

这个需要看项目规范,适合业务即可,软件最重要的还是是解决复杂度问题,如果service之间是通过接口依赖、IoC注入和自动事物机制,Service层可以互相调用。

二、领域模型


1、Model, 领域模型对象

  领域模型POJO 一般是 DO/DTO/BO/VO 的统称,即POJO 专指只有 setter / getter / toString 的简单类,一般无业务逻辑代码,包括 DO/DTO/VO/PO 等。

  • DO (Domain Object):主要用于定义与数据库对象应的属性(ORM对象关系映射),实体bean的DO映射成一张表,通过 DAO 层向上传输数据源对象。看项目的编码规约,有的习惯使用命名为XxxEntity, 有的习惯命名为XxxDo, 比如UserEntity或者UserDO           
  • DTO数据传输对象(Data Transfer Object):系统、服务之间交互传输用或者是传输类间数据, Service 或 Manager 向外传输的对象。  看项目的编码规约,比如我们习惯使用DTO来做rest 接口参数或者rpc参数。

  • VO View Object:  视图对象,用于展示层,  前台(APP\WAP\PC)展示用;即返回给前端的对象,VO可以仅向前端传输,页面需展示字段,如pageList等。
  • PO(Persistent Object):  持久化对象,就是对象是不是需要通过系列化持久化存储到数据库。 其作用就是项目的编码规约,比如我们把需要持久化保存的对象命名为PO.
  • Entity实体: 也可以用作DO,看项目的编码规约,比如我们使用Entity来做service直接的对象传递。

领域模型的后缀命名也是要结合团队工程规范:比如还有

BO(Business Object)后缀:业务对象。由Service层输出的封装业务逻辑的对象。

Query/Filter/Param后缀:数据查询对象,各层接收上层的查询请求。

Form后缀:表单提交对象

领域模型类里面到底要不要写逻辑,还是只能用最普通的get/set方法。这就是讨论失血模型、贫血模型、充血模型的问题。

2、失血模型

失血模型:domain object只有属性的get set方法的纯数据类,所有的业务逻辑完全由Service层来完成的,由于没有dao,Service直接操作数据库,进行数据持久化。

service:  肿胀的服务逻辑

model:只包含get set方法

显然失血模型service层负担太重,一般不会有这种设计。
 

3、贫血模型

领域模型是指领域对象domain ojbect里只有get和set方法(POJO),所有的业务逻辑都不包含在内而是放在Service层。

service :组合服务,也叫事务服务

model:除包含get set方法,还包含原子服务(如获得关联model的id)

dao:数据持久化

贫血模型比较常见,其问题在于原子服务往往不能直接拿到关联model,因此可以把这个原子服务变成直接用关联modelRepo拿到关联model,这就是充血模型。
 

贫血模型最早广泛应用源于EJB2,最强盛时期则是由Spring创造,将:

  • “行为”(逻辑、过程);

  • “状态”(数据,对应到语言就是对象成员变量)。

分离到不同的对象中:

  • 只有状态的对象就是所谓的“贫血对象”;

  • 只有行为的对象就是,我们常见的N层结构中的Logic/Service/Manager层(对应到EJB2中的Stateless Session Bean)。

——曾经Spring的作者Rod Johnson也承认,Spring不过是在沿袭EJB2时代的“事务脚本”,也就是面向过程编程。

贫血模型和面向对象设计背道而驰。但优点在于简单:

  • 对于只有少量业务逻辑的应用来说,使用起来非常自然;

  • 开发迅速,易于理解;

  • 注意:也不能完全排斥这种方式。

缺点无法良好的应对复杂逻辑。《重构-改善既有代码的设计》提到的坏代码味道之一就是纯数据类的问题,要求数据和行为是在一起,而贫血模型恰恰就是纯数据类的问题呢。

4、充血模型

面向对象设计的本质是:“一个对象是拥有状态和行为的”。

充血模型:绝大多业务逻辑都应该被放在domain object里面,包括持久化逻辑,而Service层是很薄的一层,仅仅封装事务和少量逻辑,不和DAO层打交道。

service :组合服务 也叫事务服务

model:除包含get set方法,还包含原子服务和数据持久化的逻辑

它的优点是面向对象,Business Logic符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑service太过沉重。

充血模型的问题也很明显,当model中包含了数据持久化的逻辑,实例化的时候可能会有很大麻烦,拿到了太多不一定需要的关联model。

如果是数据库表的映射类DO,最好是越简单越好
如果是封装查询条件、查询结果(如dto之类)是可以加一些简单逻辑在里面的。

三、项目顶层代码结构和调用链


1、 项目顶层代码结构

com  
 +- xxxCompany  
     +- myprojectApplication.java (应用启动主类)
     |
     +- myproject  
         |  
         +-common/base //公共/基础相关相关
         |  +-  AppConst.java  
         |  +-  AppContext.java  
         |   
         +-module1
         |  +---controller 控制层
         |  |   +- Userontroller.java
         |  |   
         |  +---model //模型
         |  |   +- domain 实体对象
         |  |   |  +-UserDO.java        
         |  |   |  
         |  |   +- dto 传输对象
         |  |   |  +-UserQueryDTO.java
         |  |   |  
         |  |   +- vo 前端对象
         |  |   |  +-UserProfileVO.java
         |  |   
         |  |   
         |  +---service//业务逻辑层,处理数据逻辑,验证数据
         |  |   +- impl
         |  |   |   +- UserServiceImpl.java          
         |  |   +- UserService.java
         |  |
         |  |
         |  +---dao //数据访问层,与底层 MySQL、 Oracle、 Hbase 等进行数据交互。
         |  |    +- UserDao.java
         |
         +- util//工具包
         |   +- DateUtils.java //Utils后缀 

2、调用链规约

我们项目编程规约,结合swaggger看:

1)controller层接收DTO请求参数 ,并进行简单参数校验。

2)controller层调用了Service层的接口方法。

3)Service层调用Dao层的方法,返回DO的entity对象。

4)Service层封装了前端需要VO对象,返回给controller层

5)controller层返回VO给前端。

四、常用命名原则和规约


规范命名约定目的是为了保持统一,减少沟通成本,提升团队研发效能。通过命名就能体现出代码的特征,含义或者是用途,让阅读者可以根据名称的含义快速厘清程序的脉络。做到见名知意,名副其实。

《Clean Code》这本书明确指出:

    好的代码本身就是注释,我们要尽量规范和美化自己的代码来减少不必要的注释。

    若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。

举个例子:

去掉下面复杂的注释,只需要创建一个与注释所言同一事物的函数即可

// check to see if the employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

应替换为

if (employee.isEligibleForFullBenefits())
 

1、首先遵循一定的原则:

好的命名风格应该是看到变量或者函数名字就能“望文生义”,做到名副其实。毕竟我们不能把自己写的所有代码都做注释。好的命名是不需要注释来补充的,达到代码自解释。

1)、编码规范统一:

在编写一个子模块或派生类的时候,要遵循其基类或整体模块的命名风格,保持命名风格在整个模块中的同一性。如骆驼命名法,大括号位置等。

2)、命名一致性原则:(命名的黄金原则)

    命名一致性:如booleam变量,前缀最好是is、can,need。

函数取名最忌讳"名不副实"

    比如一个 getXXX 的函数,结果里面还做了 add, update 的操作。对问题排查造成很大的困扰。因此命名一定要用实质内容相符。不论是类名,变量名,方法名都要有实际的意。   建议方法:先查查字典,找个通俗易懂而且比较贴近的名字。可以参考 jdk 的命名、通用词汇和行业词汇; 作用域小的采用短命名,作用域大的采用长命名。

  再比如有个Cache类,里面有个函数判断key是否过期:

public boolean isExpired(String key) {

        // 当前时间戳

        long curTimestamp = DateUtils.nowUnixTime();

        // 获取key的存入时间戳

        long storeTimestamp = getStoreTimestamp(key);

        

        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {

            // 注意这个地方的delete是个隐藏逻辑

            delete(key);

            return true;

        }

        return false;

 }

  上面这个函数从函数字面意思看是判断key是否过期,但是!!它居然在函数里面隐藏了一段特殊逻辑:如果过期则删除掉key。这个就是典型的"名不副实",这个是最忌讳的,会给后续的开发人员留下"巨坑"。

  有两种方式去优化这段代码:

  • 方式一:将隐藏逻辑去掉

public boolean isExpired(String key) {

        // 当前时间戳

        long curTimestamp = DateUtils.nowUnixTime();

        // 获取key的存入时间戳

        long storeTimestamp = getStoreTimestamp(key);

        

        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {

            return true;

        }

        return false;

 }

  • 方式二:改变函数名字

public int deleteIfExpired(String key) {

        // 当前时间戳

        long curTimestamp = DateUtils.nowUnixTime();

        // 获取key的存入时间戳

        long storeTimestamp = getStoreTimestamp(key);

        

        if (curTimestamp - storeTimestamp > MAX_EXPIRE_SECONDS) {

            return delete(key);

        }

        return 0;

 }

3)、方法名字标识符组成:动词+名词

1、名字标识符采用英文单词,应当直观且可以拼读,可望文知意,用词应当准确。正确的英文拼写和语法可以让阅读者易于理解,避免歧义。

2、杜绝完全不规范的英文缩写,避免望文不知义。如Function“缩写”成 Fu,此类随意缩写严重降低了代码的可阅读性。

4、为了达到代码自解释的目标,任何自定义编程元素在命名时,使用完整的单词组合来表达。
正例:在 JDK 中,对某个对象引用的 volatile 字段进行原子更新的类名为:AtomicReferenceFieldUpdater。

4、只要命名合理,不要担心方法名称太长

首先需要保证命名有意义,只要命名合理,不要担心方法名称太长,但方法名称过长常常又意味着该方法干的事太多了,则需要思考是否可以拆分方法,这也反映了"职责单一"设计原则。

保证命名有意义的前提之下,尽量保证命名的简短,删除一些不影响表达的单词,或者采用缩写。举几个例子:

  1. ActivityRuleRepository.findActivityRuleById() 可以简写成ActivityRuleRepository.findById(),因为上下文已经说明白了这个一个查询活动规则的Repository接口。
  2. void updateRuleForRevision(String ruleString) 简写成void updateRule4Revision(String ruleStr)
  3. ActivityRule convert2ActivityRule(String ruleStr) 借鉴toString的简写方式,简写成ActivityRule toActivityRule(String ruleStr)

3)最小化长度 && 最大化信息量原则

在足够描述用途的前提下,尽量以最小词汇量去描述事物,如staff就company_person_collection要简捷易记。

也可采用有广泛共识的缩写后缀,如msg,idx,cnt,btn,id ,flg等,如果缩写共识度不高,请在取得同事们共识后用注释说明其含义。

变量/函数长度控制在4~18个字符内,有助于对代码的理解。

过长的变量:howLongDoesItTakeToOpenTheDoor, howBigIsTheMaterial…

简捷的变量:timeToOpenTheDoor, materialSize.

4)避免过于相似,也不要用双关语,避免歧义,比如add和append:

不要出现仅靠大小写区分的相似的标识符,例如“i”与“I”,“function”与“Function”等等。

正确命名具有互斥意义的标识符

用正确的反义词组命名具有互斥意义的标识符 ,如

add / remove       begin / end        create / destroy

insert / delete       first / last         get / set

increment / decrement                 put / get

add / delete         lock / unlock      open / close

min / max          old / new         start / stop

next / previous      source / target     show / hide

send / receive       source / destination

cut / paste          up / down

这些有助于理解成对的变量/函数的意义.

尽量避免名字中出现数字编号

尽量避免名字中出现数字编号,如value1,value2等,除非逻辑上的确需要编号。

5)少使用类型前缀

最好从名字上就能揣测出其类型。加后缀说明是可以的。

少单独使用含义广泛的词。如data,info,value等。

6)避免过度使用get作为方法前缀

应该用更精确的动词描述动作,如“请求”request,“获取”acquire,“查找”search/lookfor/find,“查询”inquire,“构建”build 或“创建”create, 执行某个动作如执行查询doGet

get和set和成对出现,是轻量的实现。

get这种方法命名一定是明确index,性能比较好,

query选择选择符合的。

  1. 找到数据:find/lookfor,肯定有数据。
  2. 搜索数据:search、搜索数据可能不存在。
  3. 查询数据:query 强调查询这个动作,返回的数据是比较原始。query返回的是List<object>,find返回的才是List<Record>。

2.常见类名命名规范:

1、类应该是名词形式,通常由单个名词或名词短语组成,通常以名词结尾。而且在类名中要体现它是以保存数据为主还是提供功能为主。例如 ObjectBuilder 这个类我们都可以猜到它的主要功能是创建Object对象,

2、以动词-er/or 结尾的类名,至少应该包含一个以该动词开头的方法。例如 ObjectBuilder 这个类,它至少应该包含一个以 build- 开头的方法。有了这种规约,阅读者就能更方便地理解这个类。

前缀名

意义

举例

Abstract 或者 Base 开头抽象类BaseUserService

后缀名

意义

举例

Controller

对外接口类

UserController

Service

服务类,里面包含了给其他类提同业务服务的方法

UserService

Impl

实现类,而不是接口

UserServiceImpl

Manager

通用业务处理层

UserManager

Ixxx

接口

IUserDao

Dao

数据访问方法

OrderDao

Listener

响应某种事件的类

PaymentSuccessListener

Event

这个类代表了某种事件

PaymentSuccessEvent

Factory

生成某种对象工厂的类

PaymentOrderFactory

Adapter

用来连接某种以前不被支持的对象的类

DatabaseLogAdapter

Job

某种按时间运行的任务

PaymentOrderCancelJob

Wrapper

这是一个包装类,为了给某个类提供没有的能力

SelectableOrderListWrapper

DO

定义与数据库对象应的属性(ORM对象关系映射

UserDO

DTO

DTO数据传输对象

UserDTO

VO

用于展示层,  前台(APP\WAP\PC)展示用

UserVO

Entity

实体

属性

约束

举例

设计模式相关类Builder,Factory等

当使用到设计模式时,需要使用对应的设计模式作为后缀,如ThreadFactory。

Bridge:桥连模式
Builder:建造者模式
Adapter: 适配器
Decorator:装饰器
Delegation:委派模式
Facade: 外观模式
Proxy:代理模式
terator: 迭代器
Observer/Observable: 观察者模式
Strategy:策略模式
Command:命令模式

Util类一般是无状态的,只包含静态方法。使用时无需创建类的实例。
Helper类可以有状态(类的成员变量),一般需要创建实例才能使用。
处理特定功能的Handler,Predicate, Validator表示处理器,校验器,断言,这些类工厂还有配套的方法名如 handle,predicate,validate

POJO 是 DO / DTO / BO / VO 的统称,禁止命名成 xxxPOJO。

接口管理:XXXManager
线程相关:XXXExecutor
初始化相关:XXXInitializer
处理相关:XXXHandler
监听器:XXXListener
 

3、静态工厂方法名称,推荐使用以下名称:

from:

一种型转换方法,该方法接受单个参数并返回该类型的相应实例,例如:

Date d = Date.from(instant);

of:一个聚合方法,它接受多个参数并返回一个包含这些参数的此类实例,例如:

Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);

valueOf:一种替代 from 和 of 但更冗长的方法,例如:

BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);

instance 或 getInstance:返回一个实例,该实例由其参数(如果有的话)描述,但不具有相同的值,例如:

StackWalker luke = StackWalker.getInstance(options);

create 或 newInstance:与 instance 或 getInstance 类似,只是该方法保证每个调用都返回一个新实例,例如:

Object newArray = Array.newInstance(classObject, arrayLen);

4.常见方法命名规范:

规范说明

1、动词 + 名词: 和类名不同的是,方法命名一般为动词或动词短语,与参数或参数名共同组成动宾短语,即动词 + 名词,在中间体现过程,使名字简短易懂;一个好的函数名一般能通过名字直接获知该函数实现什么样的功能。

2、用具体的名字代替抽象的名字。比如:ServiceCanStart() 与 CanListenOnPort()

检测服务端口是否可以开始监听,明显后一个要更好一些。

3、保证命名有意义:首先需要保证命名有意义,只要命名合理,不要担心方法名称太长,但方法名称过长常常又意味着该方法干的事太多了,则需要思考是否可以拆分方法,这也反映了"职责单一"设计原则。

保证命名有意义的前提之下,尽量保证命名的简短,删除一些不影响表达的单词,或者采用缩写。举几个例子:

  1. UserDao.findUserById() 可以简写成UserDao.findById(),因为上下文已经说明白了这个一个查询用户的Dao接口。
  2. void updateUserForName(String name) 简写成void updateForName(String name)
  3. User  convertToUserModel(String uid) 借鉴toString的简写方式,简写成ttoUserModel(String ruleStr)

4、用词准确:命名能告诉用户这个对象/方法的意图 -- “它是什么” 和 “它能做什么”。

如add、insert、append等词都有类似的意思,在使用时最好准确,而且统一。如字符串连接应该用append,而不是add。

5、可以采用 do 动词+名词(首字母大写) 形式,比如 doPrint,DoDelete 等。

可以借鉴spring的方法命名规则:

1、getBean:轻量实现,调用doGetBean。

2、doGetBean:先判断缓存,缓存没有则调用createBean生成。

      String beanName = this.transformedBeanName(name);

      Object sharedInstance = this.getSingleton(beanName);

      this.createBean(beanName, mbd, args);

3、createBean:校验bean的合法性,然后调用doCreateBean真正创建。

    

4、doCreateBean:真正创建bean。

 具体命名说明

1) 各层的方法命名规约: Service / DAO 层的方法命名规约:
  1. 获取单个对象的方法用 get 做前缀。
  2. 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save / insert 做前缀。
  5. 删除的方法用 remove / delete 做前缀。
  6. 修改的方法用 update 做前缀。
  7. 取出数据:fetch
  8. 找到数据:find,肯定有数据。
  9. 搜索数据:search、搜索数据可能不存在。
  10. 查询数据:query 强调查询这个动作,返回的数据是比较原始。query返回的是List<object>,find返回的才是List<Record>。

2)、查找获取
    get 是广义的“获取”:和set和成对出现,是轻量的实现。这种方法命名一定是明确index或者获取属性。
    find/lookfor:肯定有数据,查询单个结果。
    search:搜索数据,查询一列符合条件的结果,搜索的数据可能不存在。
    find 表示“找到”,即终态,search 表“搜索”,即行为。
    query 查询数据:类似于 search,query 强调查询这个动作,返回的数据是比较原始。query返回的是List<object>,find返回的才是List<Record>。
但是search,find ,query不要做这种程度的细分,大部分人认为它们是同义词。
 

3) 返回真伪值的方法

单词位置意义例子
is前缀对象是否符合期待的状态isValid,isDone
can前缀 对象能否执行所期待的动作canRemove
has前缀对象是否持有所期待的数据和属性hasObservers
need前缀调用方是否需要执行某个命令或方法needMigrate
should前缀是否应当执行操作shouldCreate,shouldReset

4) 用来检查的方法

单词位置意义例子
ensure前缀检查是否为期待的状态,不是则抛出异常或返回error codeensureCapacity
validate前缀

 检查是否为正确的状态,不是则抛出异常或返回error code

用于复杂的输入校验。

validateInputs
check前缀检查简单的输入参数校验。
verify前缀强调验证:验证产品合规/符合给定规范

5)、创建/构建:

    create:工厂方法的常见命名
    generate:经过计算生成全新的对象,例如 ID。强调全新生成

6)、添加
    append 专指添加到列表末。
    insert 强调可以插入到列表的任何位置。
    add 是通用的列表添加方案,add(E) 等同于 append,add(index, E) 等同于 insert。addAll 用于批量添加。
    put 通常用于列表之外的添加场景。如 map, iostream。
    push 仅用于某些数据结构,如栈。

7)、按需求才执行的方法

单词位置意义例子
try前缀尝试执行,失败时抛出异常或是返回errorcode    tryCreatetryCreate
OrDefault后缀 尝试执行,失败时返回默认值getOrDefault
OrElse后缀尝试执行、失败时返回实际参数中指定的值getOrElse
IfNeeded后缀需要的时候执行,不需要的时候什么都不做drawIfNeeded
might同上mightCreate

8) 异步相关方法命名

单词位置意义例子
blocking前缀线程阻塞方法blockingGetUser
Async/Sync后缀异步方法sendAsync
单词位置意义例子
schedule前缀Job和Task放入队列schedule, scheduleJob
execute前缀执行异步方法 execute,executeTask
start/stop/cancel前缀启动/停止/取消/异步任务startJob

9)回调方法

单词位置意义例子
on前缀事件发生时执行

onCompleted

onSucc

onError

before前缀事件发生前执行beforeUpdate
after前缀事件发生后执行afterUpdate

10) 操作对象生命周期的方法

单词位置意义例子
init前缀

init 是 initialize 的全称,通常用来初始化一个类到可用状态。

一般使用在类生命周期层面处在正常可执行状态”

initialize前缀初始化。也可作为延迟初始化使用initialize
prepare前缀语义通常指“在业务层面做好准备”
start/begin前缀通常用于这个对象正式开始正常工作,即切换到 running 状态。
pause前缀/后缀暂停onPause ,pause
resume恢复:resume 与 pause 是成对的。
stop前缀/后缀事件发生后执行:stop/end/terminate/halt停止类运行,与 start 对应。onStop,stop
destroy前缀

销毁:

destroy/release/shutdown/teardown 是近义词

  • destroy 强调销毁对象。

  • release 强调释放资源。

  • teardown 通常与 setup 对应。

  • exit 通常指程序退出。

  • shutdown 是通用“彻底关闭”的动词。当 destroy/release 不合适时,回退到 shutdown。

destroy

11)执行/处理

单词位置意义例子
do前缀真正执行:强调真正执行某个动作,和其他动词一块使用,对应某些具体的操作类:Operator

doGet,doCreate,doDispatch

doExecute

perform前缀实施:施执行较复杂逻辑,一般单独使用perform
execute前缀执行:执行某个调用,后面跟着名词,常用对应Executor相关的,如 Job/TaskexecuteTask
process

处理:常用在Processor,数据处理(data processor)

process指对原材料,食物的加工,处理

run运行:通常对应 Runnable
handle

事件处理器Handler的处理。以某种方法、策略处理。

handle强调对人,情况,机器等的处理,对付管理等

12)、删除
    remove/delete 是同义词。严格来说,remove 指移除,即暂时从容器中取出放置在一边,delete指删除,即将对象整个清除。
    clear/cleanup 通常表示 1) 清理列表,等效于 removeAll 2)清理状态,即恢复类到初始化状态。
    pop 只在栈/队列等数据结构中使用,通常会返回对象并从数据结构中移除。

12与集合操作相关的方法:

集合的容量通常用capacity、集合实际元素个数用size、字符串长度用length,不要用size去形如字符串的长度。

单词位置意义例子
add前缀添加addJob
insert前缀插入到下标ninsertJob
append前缀附加信息到当前对象的末尾appendJob
put前缀添加与key对应的元素putJob
remove前缀移除元素 removeJob
push前缀添加到栈头pushJob
pop前缀从栈头取出并移除popJob
peek前缀从栈头取出但不移除peekJob
find前缀寻找符合条件的某物findById
contains是否持有与指定对象相同的对象contains

   

4.7 助词/介词
 

单词位置意义例子
by中间靠、通过getByName

for中间为某物做了什么setDataForVm
with中间“和”、带有的意思,如with sb.startWithA():A开头的单词
Of使用Of串联多个名词,使用形容词修饰名词
List<String> list=List.of("a","b","c","d");
in
as:返回某个视图,等价于 returnViewAs。一个“视图(View)” 通常是对原有对象的另一角度的抽象,通常会持有对原有数据的引用,而不会产生新的数据。List<T> asList();

4.8 与数据相关的方法

获取/读取/查找/查询:get、fetch、acquire、read、search、find、query
create:新创建    createAccount
from:从既有的某物新建,或是从其他的数据新建    fromConfig
to:转换    toString
update:更新既有某物    updateAccount
load: 读取    loadAccount
fetch:远程读取    fetchAccount
delete: 删除    deleteAccount
remove:删除    removeAccount
save:保存    saveAccount
store:保存    storeAccount
commit:保存    commitChange
apply: 保存或应用    applyChange
clear:清除数据或是恢复到初始状态    clearAll
reset:清除数据或是恢复到初始状态    resetAll

4.9 成对出现的动词

get获取    set 设置
add 增加    remove 删除
create 创建    destory 移除
start 启动    stop 停止
open 打开    close 关闭
read 读取    write 写入
load 载入    save 保存
create 创建    destroy 销毁
begin 开始    end 结束
backup 备份    restore 恢复
import 导入    export 导出
split 分割    merge 合并
inject 注入    extract 提取
attach 附着    detach 脱离
bind 绑定    separate 分离
view 查看    browse 浏览
edit 编辑    modify 修改
select 选取    mark 标记
copy 复制    paste 粘贴
undo 撤销    redo 重做
insert 插入    delete 移除
add 加入    append 添加
clean 清理    clear 清除
index 索引    sort 排序
find 查找    search 搜索
increase 增加    decrease 减少
play 播放    pause 暂停
launch 启动    run 运行
compile 编译    execute 执行
debug 调试    trace 跟踪
observe 观察    listen 监听
build 构建    publish 发布
input 输入    output 输出
encode 编码    decode 解码
encrypt 加密    decrypt 解密
compress 压缩    decompress 解压缩
pack 打包    unpack 解包
parse 解析    emit 生成
connect 连接    disconnect 断开
send 发送    receive 接收
download 下载    upload 上传
refresh 刷新    synchronize 同步
update 更新    revert 复原
lock 锁定    unlock 解锁
check out 签出    check in 签入
submit 提交    commit 交付
push 推    pull 拉
expand 展开    collapse 折叠
begin 起始    end 结束
start 开始    finish 完成
enter 进入    exit 退出
abort 放弃    quit 离开
obsolete 废弃    depreciate 废旧
collect 收集    aggregate 聚集

4.10其他常用词汇

application 应用程式 应用、应用程序
application framework 应用程式框架、应用框架 应用程序框架
architecture 架构、系统架构 体系结构
argument 引数(传给函式的值)。叁见 parameter 叁数、实质叁数、实叁、自变量
array 阵列 数组
assign 指派、指定、设值、赋值 赋值
assignment 指派、指定 赋值、分配
attribute 属性 属性、特性
batch 批次(意思是整批作业) 批处理
binding 系结 绑定
build 建造、构筑、建置(MS 用语)
build-in 内建 内置
business 商务,业务 业务
callback 回呼 回调
chain 串链(例 chain of function calls) 链
checked exception 可控式异常(Java)
cleanup 清理、善后 清理、清除
client 客端、客户端、客户 客户
client-server 主从架构 客户/服务器
container 容器 容器 ###### (存放资料的某种结构如 list, vector…)
context 背景关系、周遭环境、上下脉络 环境、上下文
control 控制元件、控件 控件


define 定义 预定义
definition 定义、定义区、定义式 定义
delegate 委派、委托、委任 委托

derived class 衍生类别 派生类
design by contract 契约式设计
design pattern 设计范式、设计样式 设计模式 ※ 最近我比较喜欢「设计范式」一词

driver 驱动程式 驱动(程序)
dynamic binding 动态系结 动态绑定
escape code 转义码 转义码
evaluate 评估、求值、核定 评估
event 事件 事件
event driven 事件驱动的 事件驱动的
exception 异常情况 异常
exception declaration 异常宣告(ref. C++ Primer 3/e, 11.3) 异常声明
exception handling 异常处理、异常处理机制 异常处理、异常处理机制
exception specification 异常规格(ref. C++ Primer 3/e, 11.4) 异常规范
exit 退离(指离开函式时的那一个执行点) 退出
explicit 明白的、明显的、显式 显式
export 汇出 引出、导出
expression 运算式、算式 表达式
facility 设施、设备 设施、设备
feature 特性
field 栏位,资料栏(Java) 字段, 值域(Java)
flush 清理、扫清 刷新
font 字型 字体

formal parameter 形式叁数 形式叁数
framework 框架 框架

generic algorithm 泛型演算法 通用算法
getter (相对於 setter) 取值函式
global 全域的(对应於 local) 全局的
global object 全域物件 全局对象
global scope resolution operator 全域生存空间(范围决议)运算子 :: 全局范围解析操作符
group 群组

handle 识别码、识别号、号码牌、权柄 句柄
handler 处理常式 处理函数
heap 堆积 堆

hook 挂钩 钩子

inheritance 继承、继承机制 继承、继承机制
inline 行内 内联
inline expansion 行内展开 内联展开
initialization 初始化(动作) 初始化
initialization list 初值列 初始值列表
initialize 初始化 初始化

invoke 唤起 调用

call: 比较明确的调用 (方法名,形参,返回值都是确定的)
invoke: 调用的对象,方法名,参数,返回值都不确定,比较上层建筑的代码常用。
e.g jdk生成动态代理,设计动态代理类是上层建筑代码,负责生成 接口的对象,对象中的所有方法都是调用 invoke方法,来让目标对象做工作。
让哪个目标对象?, 执行那个方法(Method类),方法传参(args)都是未知不确定的,所以用invoke 祈求(本身祈求就是很虚的东西呀),更加符合当前代码的抽象场景

iterate 迭代(回圈一个轮回一个轮回地进行) 迭代


load 载入 装载
loader 载入器 装载器、载入器
local 区域的(对应於 global) 局部的
local object 区域物件 局部对象
lock 机锁
loop 回圈 循环
lvalue 左值 左值
macro 巨集 宏
magic number 魔术数字 魔法数

member 成员 成员

message 讯息 消息

multi-tasking 多工 多任务

postfix 后置式、后序式 后置式

prefix 前置式、前序式 前置式
preprocessor 前处理器 预处理器


schedule 排程 调度
scheduler 排程器 调度程序
scheme 结构纲目、组织纲目

5. 常见变量名命名规范:

命名原则1:名词、名字组合或者形容词

变量一般情况下建议使用名词、名字组合或者形容词,因为变量一般形容的是一种事物或者事物的属性,所以用名词或者名词组合更容易让人理解。

命名原则2:变量命名前后用词需统一

  在同一个工程或者一个场景下,变量命名风格需前后统一,比如total和sum都能表示总计的意思,那么所有需要用到"总计"含义的地方要么全部使用total、要么全部使用sum。

1、在常量与变量命名时,表示类型的名词放在词尾,以提升辨识度。

正例: startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
反例: startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD
在一个方法中将返回值命名为 result,能够让方法的脉络更清晰:userListResult
 
2、【强制】 POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
说明: 本文 MySQL 规约中的建表约定第 1 条,表达是与否的变量采用 is_xxx 的命名方式,所以需要在<resultMap>设置从 is_xxx 到 xxx 的映射关系。
反例: 义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析时,“误以为 ”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

3、常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

4、集合变量用类型或者复数s作为后缀

  在java中,有很多集合,比如List、Map、Set等,那么集合变量该怎么命名呢?

  一般可采取两种方式:

  • 使用复数s结尾

List<Student> students = new ArrayList<>();

  • 用集合类型作为后缀

List<Student> studentList = new ArrayList<>();

  上面两种方式均可,没有比较明显的偏好,根据实际场景决定。第一种方式相对更简洁,第二种在局部作用域里面有多种相关的集合变量时区分度更大,比如:

List<Student> studentList = new ArrayList<>();

Map<Long, Student> studentMap = Maps.newHashMap();

for (Student stu : studentList) {

  studentMap.put(stu.getId, stu);

}

  建议是

         如果局部作用域只有一种类型的集合,那么推荐使用复数形式;

        如果局部作用域有多个相关的集合类型,那么推荐用类型结尾。

变量命名约定俗称的缩写:

全称缩写
identificationid
averageavg
maximummax
minimummin
bufferbuf
errorerr
messagemsg
imageimg
lengthlen
librarylib
passwordpwd
positionpos
data transfer objectdto
view objectvo

7)抛弃掉flag变量

五、常见开源框架的命名


管理应用类

借鉴了最流行的Java接开源软件(spring系列,netty,libgdx,guava,logback等等),总结了10类常见的类命名。

Bootstrap,Starter

一般作为程序启动器使用,或者作为启动器的基类。通俗来说,可以认为是main函数的入口。

AbstractBootstrap
ServerBootstrap
MacosXApplicationStarter
DNSTaskStarter

Processor

某一类功能的处理器,用来表示某个处理过程,是一系列代码片段的集合。如果你不知道一些顺序类的代码怎么命名,就可以使用它,显得高大上一些。

CompoundProcessor
BinaryComparisonProcessor
DefaultDefaultValueProcessor

Manager

对有生命状态的对象进行管理,通常作为某一类资源的管理入口。

AccountManager
DevicePolicyManager
TransactionManager

Holder

表示持有某个或者某类对象的引用,并可以对其进行统一管理。多见于不好回收的内存统一处理,或者一些全局集合容器的缓存。

QueryHolder
InstructionHolder
ViewHolder

Factory

毫无疑问,工厂模式的命名,耳熟能详。尤其是Spring中,多不胜数。

SessionFactory
ScriptEngineFactory
LiveCaptureFactory

Provider

Provider = Strategy + Factory Method。它更高级一些,把策略模式和方法工厂揉在了一块,让人用起来很顺手。Provider一般是接口或者抽象类,以便能够完成子实现。

AccountFeatureProvider
ApplicationFeatureProviderImpl
CollatorProvider

Registrar

注册并管理一系列资源。

ImportServiceRegistrar
IKryoRegistrar
PipelineOptionsRegistrar

Engine

一般是核心模块,用来处理一类功能。引擎是个非常高级的名词,一般的类是没有资格用它的。

ScriptEngine
DataQLScriptEngine
C2DEngine

Service

某个服务。太简单,不忍举例。范围太广,不要滥用哦。

IntegratorServiceImpl
ISelectionService
PersistenceService

Task

某个任务。通常是个runnable.

WorkflowTask
FutureTask
ForkJoinTask

传播类命名

为了完成一些统计类或者全局类的功能,有些参数需要一传到底。传播类的对象就可以通过统一封装的方式进行传递,并在合适的地方进行拷贝或者更新。

Context

如果你的程序执行,有一些变量,需要从函数执行的入口开始,一直传到大量子函数执行完毕之后。这些变量或者集合,如果以参数的形式传递,将会让代码变得冗长无比。这个时候,你就可以把变量统一塞到Context里面,以单个对象的形式进行传递

在Java中,由于ThreadLocal的存在,Context甚至可以不用在参数之间进行传递。

AppContext
ServletContext
ApplicationContext

Propagator

传播,繁殖。用来将context中传递的值进行复制,添加,清除,重置,检索,恢复等动作。通常,它会提供一个叫做propagate的方法,实现真正的变量管理。

TextMapPropagator
FilePropagator
TransactionPropagator

回调类命名

使用多核可以增加程序运行的效率,不可避免的引入异步化。我们需要有一定的手段,获取异步任务执行的结果,对任务执行过程中的关键点进行检查。回调类API可以通过监听、通知等形式,获取这些事件。

Handler,Callback,Trigger,Listener

callback通常是一个接口,用于响应某类消息,进行后续处理;Handler通常表示持有真正消息处理逻辑的对象,它是有状态的;tigger触发器代表某类事件的处理,属于Handler,通常不会出现在类的命名中;Listener的应用更加局限,通常在观察者模式中用来表示特定的含义。

ChannelHandler
SuccessCallback
CronTrigger
EventListener

Aware

Aware就是感知的意思,一般以该单词结尾的类,都实现了Aware接口。拿spring来说,Aware 的目的是为了让bean获取spring容器的服务。具体回调方法由子类实现,比如ApplicationContextAware。它有点回调的意思。

ApplicationContextAware
ApplicationStartupAware
ApplicationEventPublisherAware

监控类命名

现在的程序都比较复杂,运行状态监控已经成为居家必备之良品。监控数据的收集往往需要侵入到程序的边边角角,如何有效的与正常业务进行区分,是非常有必要的。

Metric

表示监控数据。不要用Monitor了,比较丑。

TimelineMetric
HistogramMetric
Metric

Estimator

估计,统计。用于计算某一类统计数值的计算器。

ConditionalDensityEstimator
FixedFrameRateEstimator
NestableLoadProfileEstimator

Accumulator

累加器的意思。用来缓存累加的中间计算结果,并提供读取通道。

AbstractAccumulator
StatsAccumulator
TopFrequencyAccumulator

Tracker

一般用于记录日志或者监控值,通常用于apm中。

VelocityTracker
RocketTracker
MediaTracker

内存管理类命名

如果你的应用用到了自定义的内存管理,那么下面这些名词是绕不开的。比如Netty,就实现了自己的内存管理机制。

Allocator

与存储相关,通常表示内存分配器或者管理器。如果你得程序需要申请有规律得大块内存,allocator是你得不二选择。

AbstractByteBufAllocator
ArrayAllocator
RecyclingIntBlockAllocator

Chunk

表示一块内存。如果你想要对一类存储资源进行抽象,并统一管理,可以采用它。

EncryptedChunk
ChunkFactory
MultiChunk

Arena

英文是舞台、竞技场的意思。由于Linux把它用在内存管理上发扬光大,它普遍用于各种存储资源的申请、释放与管理。为不同规格的存储chunk提供舞台,好像也是非常形象的表示。

关键是,这个词很美,作为后缀让类名显得很漂亮。

BookingArena
StandaloneArena
PoolArena

Pool

表示池子。内存池,线程池,连接池,池池可用。

ConnectionPool
ObjectPool
MemoryPool

过滤检测类命名

程序收到的事件和信息是非常多的,有些是合法的,有些需要过滤扔掉。根据不同的使用范围和功能性差别,过滤操作也有多种形式。你会在框架类代码中发现大量这样的名词。

Pipeline,Chain

一般用在责任链模式中。Netty,Spring MVC,Tomcat等都有大量应用。通过将某个处理过程加入到责任链的某个位置中,就可以接收前面处理过程的结果,强制添加或者改变某些功能。就像Linux的管道操作一样,最终构造出想要的结果。

Pipeline
ChildPipeline
DefaultResourceTransformerChain
FilterChain

Filter

过滤器,用来筛选某些满足条件的数据集,或者在满足某些条件的时候执行一部分逻辑。如果和责任链连接起来,则通常能够实现多级的过滤。

FilenameFilter
AfterFirstEventTimeFilter
ScanFilter

Interceptor

拦截器,其实和Filter差不多。不过在Tomcat中,Interceptor可以拿到controller对象,但filter不行。拦截器是被包裹在过滤器中。

HttpRequestInterceptor

Evaluator

英文里是评估器的意思。可用于判断某些条件是否成立,一般内部方法evaluate会返回bool类型。比如你传递进去一个非常复杂的对象,或者字符串,进行正确与否的判断。

ScriptEvaluator
SubtractionExpressionEvaluator
StreamEvaluator

Detector

探测器。用来管理一系列探测性事件,并在发生的时候能够进行捕获和响应。比如Android的手势检测,温度检测等。

FileHandlerReloadingDetector
TransformGestureDetector
ScaleGestureDetector

结构类命名

除了基本的数据结构,如数组、链表、队列、栈等,其他更高一层的常见抽象类,能够大量减少大家的交流,并能封装常见的变化。

Cache

这个没啥好说的,就是缓存。大块的缓存。常见的缓存算法有LRU、LFU、FIFO等。

LoadingCache
EhCacheCache

Buffer

buffer是缓冲,不同于缓存,它一般用在数据写入阶段。

ByteBuffer
RingBuffer
DirectByteBuffer

Composite

将相似的组件进行组合,并以相同的接口或者功能进行暴露,使用者不知道这到底是一个组合体还是其他个体。

CompositeData
CompositeMap
ScrolledComposite

Wrapper

用来包装某个对象,做一些额外的处理,以便增加或者去掉某些功能。

IsoBufferWrapper
ResponseWrapper
MavenWrapperDownloader

Option, Param,Attribute

用来表示配置信息。说实话,它和Properties的区别并不大,但由于Option通常是一个类,所以功能可以扩展的更强大一些。它通常比Config的级别更小,关注的也是单个属性的值。Param一般是作为参数存在,对象生成的速度要快一些。

SpecificationOption
SelectOption
AlarmParam
ModelParam

Tuple

元组的概念。由于Java中缺乏元组结构,我们通常会自定义这样的类。

Tuple2
Tuple3

Aggregator

聚合器,可以做一些聚合计算。比如分库分表中的sum,max,min等聚合函数的汇集。

BigDecimalMaxAggregator
PipelineAggregator
TotalAggregator

Iterator

迭代器。可以实现Java的迭代器接口,也可以有自己的迭代方式。在数据集很大的时候,需要进行深度遍历,迭代器可以说是必备的。使用迭代器还可以在迭代过程中安全的删除某些元素。

BreakIterator
StringCharacterIterator

Batch

某些可以批量执行的请求或者对象。

SavedObjectBatch
BatchRequest

Limiter

限流器,使用漏桶算法或者令牌桶来完成平滑的限流。

DefaultTimepointLimiter
RateLimiter
TimeBasedLimiter

网络类命名

网络编程的同学,永远绕不过去的几个名词。

Packet
通常用于网络编程中的数据包。

DhcpPacket
PacketBuffer

Protocol

同样用户网络编程中,用来表示某个协议。

RedisProtocol
HttpProtocol

Encoder、Decoder、Codec

编码解码器

RedisEncoder
RedisDecoder
RedisCodec

Request,Response

一般用于网络请求的进和出。如果你用在非网络请求的方法上,会显得很怪异。

 

其他

Util,Helper

都表示工具类,Util一般是无状态的,Helper以便需要创建实例才能使用。但是一般没有使用Tool作为后缀的。

HttpUtil
TestKeyFieldHelper
CreationHelper

Mode,Type

看到mode这个后缀,就能猜到这个类大概率是枚举。它通常把常见的可能性都列到枚举类里面,其他地方就可以引用这个Mode。

OperationMode
BridgeMode
ActionType

Invoker,Invocation

invoker是一类接口,通常会以反射或者触发的方式,执行一些具体的业务逻辑。通过抽象出invoke方法,可以在invoke执行之前对入参进行记录或者处理;在invoke执行之后对结果和异常进行处理,是AOP中常见的操作方式。

MethodInvoker
Invoker
ConstructorInvocation

Initializer

如果你的应用程序,需要经过大量的初始化操作才能启动,那就需要把它独立出来,专门处理初始化动作。

MultiBackgroundInitialize
ApplicationContextInitializer

Feture,Promise

它们都是用在多线程之间的,进行数据传递。

Feture相当于一个占位符,代表一个操作将来的结果。一般通过get可以直接阻塞得到结果,或者让它异步执行然后通过callback回调结果。

但如果回调中嵌入了回调呢?如果层次很深,就是回调地狱。Java中的CompletableFuture其实就是Promise,用来解决回调地狱问题。Promise是为了让代码变得优美而存在的。

Selector

根据一系列条件,获得相应的同类资源。它比较像Factory,但只处理单项资源。

X509CertSelector
NodeSelector

Reporter

用来汇报某些执行结果。

ExtentHtmlReporter
MetricReporter

Constants

一般用于常量列表。

Accessor

封装了一系列get和set方法的类。像lombok就有Accessors注解,生成这些方法。但Accessor类一般是要通过计算来完成get和set,而不是直接操作变量。这适合比较复杂的对象存取服务。

ComponentAccessor
StompHeaderAccessor

Generator

生成器,一般用于生成代码,生成id等。

CodeGenerator
CipherKeyGenerator

六、mybatisplus的Iservice规范命名例子:


/**
 * 顶级 Service
 *
 * @author hubin
 * @since 2018-06-23
 */
public interface IService<T> {

    /**
     * 插入一条记录(选择字段,策略插入)
     *
     * @param entity 实体对象
     */
    boolean save(T entity);

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, 1000);
    }

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, 1000);
    }

    /**
     * 批量修改插入
     *
     * @param entityList 实体对象集合
     * @param batchSize  每次的数量
     */
    boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    boolean removeById(Serializable id);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    boolean removeByMap(Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    boolean remove(Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID 批量删除)
     *
     * @param idList 主键ID列表
     */
    boolean removeByIds(Collection<? extends Serializable> idList);

    /**
     * 根据 ID 选择修改
     *
     * @param entity 实体对象
     */
    boolean updateById(T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    boolean update(T entity, Wrapper<T> updateWrapper);

    /**
     * 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
     *
     * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
     */
    default boolean update(Wrapper<T> updateWrapper) {
        return update(null, updateWrapper);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, 1000);
    }

    /**
     * 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     * @param batchSize  更新批次数量
     */
    boolean updateBatchById(Collection<T> entityList, int batchSize);

    /**
     * TableId 注解存在更新记录,否插入一条记录
     *
     * @param entity 实体对象
     */
    boolean saveOrUpdate(T entity);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T getById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表
     */
    Collection<T> listByIds(Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    Collection<T> listByMap(Map<String, Object> columnMap);

    /**
     * 根据 Wrapper,查询一条记录 <br/>
     * <p>结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default T getOne(Wrapper<T> queryWrapper) {
        return getOne(queryWrapper, true);
    }

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param throwEx      有多个 result 是否抛出异常
     */
    T getOne(Wrapper<T> queryWrapper, boolean throwEx);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    Map<String, Object> getMap(Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper,查询一条记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    int count(Wrapper<T> queryWrapper);

    /**
     * 查询总记录数
     *
     * @see Wrappers#emptyWrapper()
     */
    default int count() {
        return count(Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<T> list(Wrapper<T> queryWrapper);

    /**
     * 查询所有
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<T> list() {
        return list(Wrappers.emptyWrapper());
    }

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default IPage<T> page(IPage<T> page) {
        return page(page, Wrappers.emptyWrapper());
    }

    /**
     * 查询列表
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

    /**
     * 查询所有列表
     *
     * @see Wrappers#emptyWrapper()
     */
    default List<Map<String, Object>> listMaps() {
        return listMaps(Wrappers.emptyWrapper());
    }

    /**
     * 查询全部记录
     */
    default List<Object> listObjs() {
        return listObjs(Function.identity());
    }

    /**
     * 查询全部记录
     *
     * @param mapper 转换函数
     */
    default <V> List<V> listObjs(Function<? super Object, V> mapper) {
        return listObjs(Wrappers.emptyWrapper(), mapper);
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default List<Object> listObjs(Wrapper<T> queryWrapper) {
        return listObjs(queryWrapper, Function.identity());
    }

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

    /**
     * 翻页查询
     *
     * @param page         翻页对象
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

    /**
     * 无条件翻页查询
     *
     * @param page 翻页对象
     * @see Wrappers#emptyWrapper()
     */
    default IPage<Map<String, Object>> pageMaps(IPage<T> page) {
        return pageMaps(page, Wrappers.emptyWrapper());
    }

    /**
     * 获取对应 entity 的 BaseMapper
     *
     * @return BaseMapper
     */
    BaseMapper<T> getBaseMapper();

    /**
     * 以下的方法使用介绍:
     *
     * 一. 名称介绍
     * 1. 方法名带有 query 的为对数据的查询操作, 方法名带有 update 的为对数据的修改操作
     * 2. 方法名带有 lambda 的为内部方法入参 column 支持函数式的
     *
     * 二. 支持介绍
     * 1. 方法名带有 query 的支持以 {@link ChainQuery} 内部的方法名结尾进行数据查询操作
     * 2. 方法名带有 update 的支持以 {@link ChainUpdate} 内部的方法名为结尾进行数据修改操作
     *
     * 三. 使用示例,只用不带 lambda 的方法各展示一个例子,其他类推
     * 1. 根据条件获取一条数据: `query().eq("column", value).one()`
     * 2. 根据条件删除一条数据: `update().eq("column", value).remove()`
     *
     */

    /**
     * 链式查询 普通
     *
     * @return QueryWrapper 的包装类
     */
    default QueryChainWrapper<T> query() {
        return new QueryChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式查询 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包装类
     */
    default LambdaQueryChainWrapper<T> lambdaQuery() {
        return new LambdaQueryChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式更改 普通
     *
     * @return UpdateWrapper 的包装类
     */
    default UpdateChainWrapper<T> update() {
        return new UpdateChainWrapper<>(getBaseMapper());
    }

    /**
     * 链式更改 lambda 式
     * <p>注意:不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包装类
     */
    default LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return new LambdaUpdateChainWrapper<>(getBaseMapper());
    }
}

具体编程规约可以参考:

编程规约:https://github.com/alibaba/p3c  阿里巴巴Java开发手册(终极版).pdf
规约插件地址:https://github.com/alibaba/p3c
Eclipse Plugin:https://github.com/alibaba/p3c/tree/master/eclipse-plugin
Idea Plugin:https://github.com/alibaba/p3c/tree/master/idea-plugin
P3C-PMD:https://github.com/alibaba/p3c/tree/master/p3c-pmd
顶层设计参考《阿里巴巴Java开发手册(终极版)》第六节-工程结构。
顶层文件结构的demo.

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hguisu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值