1. 分支开发流程
- GIT分支开发规范,具体请参考:http://www.jianshu.com/p/cbd8cf5e232d。
2. 代码规范
- 主要使用Java语言来进行开发,严格遵循Sun公司的Java编码规范。此外还有以下补充。
2.1 大小写
- 常量的字母 全部大写,单词之间用一个 下划线字符(_)进行分隔。
- 除常量外的命名 采用大小写混合,提高名字的可读性。
- 一般采用小写字母,但是类和接口的名字的首字母,以及任何中间单词的 首字母应该大写。
2.2 编码格式
- 确保IDE工作环境使用UTF-8编码格式。
- 缩进一律使用空格而非Tab。
- 缩进一律采用4空格。可以设置IDE 1Tab=4空格。
- 对于非第一次创建的代码文件,禁止做reformate操作,这个操作会破坏代码的修改历史,造成大概率的push、merge、pull等操作冲突。
2.3 常量定义
- 对具有特殊意义的数字或字符要以常量的形式定义,并说明常量所表示的意义,避免幻数的出现。
2.4 统一定义公共常量
- 对于一些公共常量的使用,应该单独定义相应的常量类,并统一使用,避免在各业务类、功能类中分别定义、直接使用。
2.5 命名规则
-
Package:
1.1 Package: 每一个包的名称总是小写,暂定将com.keegoo.+(模块名)作为总前缀,比如com.keegoo.demo.blog.service、com.keegoo.demo.blog.dto;
1.2 明确不同业务建议建立子package,比如有关User业务的:com.keegoo.user.account.service、com.keegoo.user.account.dto; -
Class类:类名必须是名词,且以大写字母开头。类名应该简单清晰。
-
Interface类:
3.1 Interface命名上,暂定以XxxService的形式提供;
3.2 最终提供的接口应以Java Interface的形式出现;
3.3 每个独立的业务的Interface应该有独立的package包,一个package下可包含该业务模块下的多个Interface;
3.4 每个Interface的一个方法对应所谓的一个“中间层服务接口”,需要按照规范书写该接口的说明; -
缓存命名规则:对缓存的key要采用命名空间,以避免冲突,同时要考虑缓存的命中率,不要浪费缓存的空间。
2.6 Controller,Interface类,接口实现类编写注意事项
- Controller类里不能出现业务逻辑,只能直接调用Service类,不能直接调用Biz类(Biz类是对业务逻辑的实现封装,不依赖dao层,可独立做单元测试),manager类则是作为service、biz、dao三层的粘合剂,来协调三者执行关系。
- Interface接口定义类只定义方法,方法的实现要在实现类里完成,interface接口以xxxService结尾,接口实现类以xxxServiceImpl结尾。
2.7 注释
- 方法注释:每个接口、方法应该有一个方法的整体说明,包括方法实现的功能、参数的详细含义、返回值的取值及其详细含义。
- 类注释:对于工具类或者流程类,需要在类的注释中对于类的功能进行详细的说明;对于Model类,要对其重要的属性添加注释,注明其含义,必要时要重载hashCode和equals,toString方法。
3. 设计规范
3.1 对功能性模块的抽象
业务模块的开发过程中,应该避免过长的方法,进行合理的功能模块的提取以及抽象,避免同一段代码、类似代码到处copy的情况。
3.2 单一职责原则SRP(Single Responsibility Principle)
所谓单一职责原则,指的就是:一个类应该仅有一个引起它变化的原因。
这里变化的原因就是 所说的“职责”,如果一个类有多个引起它变化的原因,那么也就意味着这个类有多个职责,再进一步说,就是把多个职责耦合在一起了
。这会造成职责的相互影响,可能一个职责的变化,会影响到其它职责的实现,甚至引起其它职责跟着变化,这种设计是很脆弱的。
这个原则看起来是最简单和最好理解的,但是实际上是很难完全做到的,难点在于如何区分这个“职责”
,这是个没有标准量化的东西,哪些算职责,到底这个职责有多大的粒度,这个职责如何细化等等。因此,在实际开发中,这个原则也是最容易违反的。
3.3 开放-关闭原则OCP(Open-Closed Principle)
所谓开放-关闭原则,指的就是:一个类应该对扩展开放,对修改关闭。一般也被简称为开闭原则,开闭原则是设计中非常核心的一个原则。
开闭原则要求的是,类的行为是可以扩展的
,而且是在不修改已有的代码的情况下进行扩展,也不必改动已有的源代码或者二进制代码。
看起来好像是矛盾的,怎么样才能实现呢?
实现开闭原则的关键就在于合理的抽象,分离出变化与不变化的部分,为变化的部分预留下可扩展的方式
,比如:钩子方法、或是动态组合对象等等。
这个原则看起来也很简单,但事实上,一个系统要全部做到遵守开闭原则,几乎是不可能的
,也没这个必要。适度的抽象可以提高系统的灵活性,使其可扩展、可维护,但是过度的抽象,会大大增加系统的复杂程度
。应该在需要改变的地方应用开闭原则就可以了,而不用到处使用,从而陷入过度设计。
3.4 里氏替换原则LSP(Liskov Substitution Principle)
所谓里氏替换原则,指的就是:子类型必须能够替换掉它们的父类型。这很明显是一种多态的使用情况,它可以避免在多态的应用中,出现某些隐蔽的错误。
事实上,当一个类继承了另外一个类,那么子类就拥有了父类中可以继承下来的属性和操作,理论上来说,此时使用子类型去替换掉父类型,应该不会引起原来使用父类型的程序出现错误。
但是,很不幸的是,在某些情况下是会出现问题的,比如:如果子类型覆盖了父类型的某些方法,或者是子类型修改了父类型某些属性的值,那么原来使用父类型的程序就可能会出现错误
,因为在运行期间,从表面上看,它调用的是父类型的方法,需要的是父类型方法实现的功能,但是实际运行调用的确是子类型覆盖实现的方法,而该方法跟父类型的方法并不一样,那就会导致错误的产生
。
从另外一个角度来说,里氏替换原则是实现开闭的主要原则之一,开闭原则要求对扩展开放,扩展的一个实现手段就是使用继承,而里氏替换原则是保证子类型能够正确替换父类型,只有能正确替换,才能实现扩展,否则扩展了也会出现错误
。
3.5 依赖倒置原则DIP(Dependence Inversion Principle)
所谓依赖倒置原则,指的就是:
要依赖于抽象,不要依赖于具体类
。要做到依赖倒置,典型的应该做到:
高层模块不应该依赖于底层模块
,二者都应该依赖于抽象;抽象不应该依赖于具体实现
,具体实现应该依赖于抽象;
很多人觉得,层次化调用的时候,应该是高层调用“底层所拥有的接口”,这是一种典型的误解。事实上,一般高层模块包含对业务功能的处理和业务策略选择,应该被重用,应该是高层模块去影响底层的具体实现。
因此,这个底层的接口,应该是由高层提出的,然后由底层实现的,也就是说底层的接口的所有权在高层模块
,因此是一种所有权的倒置。
倒置接口所有权,这就是著名的Hollywood(好莱坞)原则:不要找我们,我们会联系你。
3.6 接口隔离原则ISP(Interface Segregation Principle)
所谓接口隔离原则,指的就是:
不应该强迫客户依赖于他们不用的方法
。
这个原则用来处理那些比较“庞大”的接口,这种接口通常会有较多的操作声明,涉及到很多的职责。客户在使用这样的接口的时候,通常会有很多它不需要的方法,这些方法对于客户来讲,就是一种接口污染,相当于强迫用户在一大堆“垃圾方法”里面去寻找他需要的方法。
因此,这样的接口应该被分离,应该按照不同的客户需要来分离成为针对客户的接口,这样的接口里面,只包含客户需要的操作声明,这样既方便了客户的使用,也可以避免因误用接口而导致的错误。
分离接口的方式,除了直接进行代码分离之外,还可以使用委托来分离接口,在能够支持多重继承的语言里面,还可以采用多重继承的方式进行分离。
3.7 最少知识原则(Least Knowledge Principle)
所谓最少知识原则,指的就是:
只和你的朋友谈话
。
这个原则用来指导我们在设计系统的时候,应该尽量减少对象之间的交互,对象只和自己的朋友谈话,也就是只和自己的朋友交互,从而松散类之间的耦合
。通过松散类之间的耦合来降低类之间的相互依赖,这样在修改系统的某一个部分时候,就不会影响其它的部分,从而使得系统具有更好的可维护性。
那么究竟哪些对象才能被当作朋友呢?最少知识原则提供了一些指导:
当前对象本身;
通过方法的参数传递进来的对象;
当前对象所创建的对象;
当前对象的实例变量所引用的对象;
方法内所创建或实例化的对象;
总之,最少知识原则要求我们的方法调用,必须保持在一定的界限范围之内,尽量减少对象的依赖关系。
3.8 其它原则
除了上面提到的这些原则,还有一些大家都熟知的原则,比如:
面向接口编程;
优先使用组合/聚合,而非继承;
当然也还有很多不是很熟悉的原则,比如:
一个类需要的数据应该隐藏在类的内部;
类之间应该零耦合,或者只有传导耦合,换句话说,类之间要么没有关系,要么只使用另一个类的接口提供的操作;
在水平方向上尽可能统一的分布系统功能;
4. 日志规范
根据日志的严重程度和功能所划分成以下五种:
-
FETA:致命的错误,程序不能或不应该正常运行。FETAL级别的日志主要用于记录非常严重而导致程序不能正常运行的错误,比如得不到FileSystem对象、Job运行失败、ODFS写重要的数据文件不成功等。
-
ERROR:严重的错误,但不影响程序流程,Tool可以继续运行。ERROR级别的日志主要用于记录虽然严重但不至于影响程序运行的错误,比如Parser解析失败等。代码中捕获异常后输出的日志一部分会属于ERROR级别。
-
WARN: 警告日志,不影响程序流程,Tool可以继续运行。WARN级别的日志与ERROR级别的日志有一些相似,但通常是不能准确判断是否出现问题,需要人来判 断,比如某轮待抓取的数据为0;而ERROR级别的日志通常是在程序运行过程中就可以发现代码、流程或数据等的某一方面或几方面存在问题。
-
INFO:信息日志,不影响程序流程,Tool可以继续运行。INFO级别的日志主要用来记录一些需要统计的信息,比如抓取统计,以及一些必要的诸如程序启停等信息。
-
DEBUG:调试日志,不影响程序流程,Tool可以继续运行。DEBUG级别的日志主要用于记录调试所用到的信息,在线上运行的过程中该级别的日志不会输出。
设计这些日志级别是为了方便的对日志进行过滤,对于线上系统,一般会将日志级别设置为WARN
,这是为了让维护人员能够根据日志迅速判断问题,另一方面不会因为频繁的写磁盘也带来性能问题。而在查找具体问题时,可能要将日志级别调到DEBUG
,希望通过更多的输出信息来定位问题。
5. 日志格式
日志格式指的是输出到日志文件的每一条日志所遵循的格式。为方便起见,每一条日志格式都由若干个“key=value”这样的属性对组成,不同的属性对之间用制表符(即“\t”)分隔。有几个特殊的属性稍有区别,不采用“key=value”这样的形式:
- 时间:时间是一条日志的第一项。而由于outlog会采用“yyyyMMddHHmmssSS”这样的格式自动记录日志的时间,所以无需再增加“key=value”这种格式的时间。
- 级别:级别是一条日志的第二项。级别采用“[LEVEL]”这种形式,如“[INFO]”。
- 消息体:消息体是一条日志的最后一项。为更直观,消息体也不采用“key=value”的格式,而直接输出,如“This is a message.”。
除此之外的其他属性均采用“key=value”的格式。这些属性对位于级别和消息体之间,以制表符(即“\t”)分隔,无需排序。对于“key=value”中的“key”,有一些常用的值可以定义为常量,如“CLASS(类名)”、“ERROR(错误类型)”等,对于非常见或特殊需求的“key”,可以在记log的时候自己定义,解析的时候注意保持一致即可。 属性中还有一个特殊的属性,即“ERROR(错误类型)”,用于表示错误的类型。ERROR和FETAL级别的日志中需要包含该属性,其他级别的日志中无需包括。“ERROR”属性的取值会有一个固定的范围,包括但不限于以下几种:
- IOException
- ConnectionRefused
网友评论