Java

代码命名最佳实践:类品用英文简称,包名用英文全称

java代码中包、类的命名一直是很纠结人的东西,也是体现代码能力的一个指标。比如 命名的其中一个纠结点是, 如何在命名中体现当前所处的系统或者业务模块。 1. 不体现。比如交易通知获取服务的请求就叫Service 2. 忠实体现,如NotifyFetchTradeService 3. 以简称方式体现,比如NfTradeService 具体怎么叫,不仅影响代码的可理解性,更重要的是,会影响在IDE里迅速定位这个类的效率  分析: 1. 如果只叫Service,在IDE里查找特定的service时就会比较麻烦。因为以Service开类的类会非常多,如果你习惯把这个模块的服务命名为Service,那你大概也会把另一个模块的服务类也命名为Service.这就导致你每次查找特定的Service时都会看到一个很长的列表,效率受损。 2. 如果叫NotifyFetchTradeService,则不存在命名冲突问题。 但你在IDE里查找时要键入很多字符;更严重的是,由于拼写太长, 你会常常想不起它的全称,正如你想不起来"FIFA"的全称一样 3. 使用简称NfTradeService,则是可以接受的折衷,命名冲突少,也容易记。不过,它仍有一个缺陷,即系统的新手看到NfTradeService类,而类注释又很简略时,则可能不知所云:“什么叫Nf”? 也就是说, 简称降低了可理解性。 补充方案就是,在包名里使用全称,比如 com.mycompany.notify.fetch; notify.fetch对新手来说,会比nf友好的多。  总结:   类在命名时要尽量体现本类所处的业务模块,同时保证这个类在IDE里可被迅速定位。 通过简称类名+全称包名,可以实现这个目标。 p.s. 补充一点:对外的web service或remote service应该叫什么? 建议叫NfwService或NfrService, 其中的w/r代表web/remote

收集一些关于eclipse code template的资料

通过eclipse code template生成代码,可以提高开发效率,并且降低重复键入代码的不幸福感 这里收集一些资料,介绍这个东西,以及常用的模板 http://eclipse.dzone.com/news/effective-eclipse-dont-write-c http://eclipse.dzone.com/news/visual-guide-templates-eclipse http://eclipse.dzone.com/news/effective-eclipse-custom-templ http://stackoverflow.com/questions/1028858/useful-eclipse-java-code-templates http://www.sproutee.com/mytemplates.xml

未必是好习惯:方法的返回类型用List,而不是ArrayList

如果你使用java超过一年,你可能会这样定义方法的签名: List getList(){ List list = new ArrayList(); … return list; } 也就是说,你会让方法返回抽象的List,而不是具体的ArrayList, 这样你可以获得比较强的封装性。 然而,这种强封装同时意味着损失了一定的信息量。 当客户端拿这个list对象时,它不知道这个list是不是modifiable的,也不知道执行list.get(n)的时间复杂度是O(1) (链表)、还是O(n) (数组)。 所以,当有必要向客户端透露返回值的特性时,直接用具体类型会更好些: ArrayList getList(){ ArrayList list = new ArrayList(); … return list; }

与其把类命名为ABCService, 不如叫AbcService

ABC是个专有名词,一般情况下对应的service应该叫做ABCService; 然而,我建议还是叫AbcService,否则对应的实例名会很别扭: private ABCService aBCService; public void setABCService(ABCService aBCService) { this.aBCService = aBCService; } 你会说变量名可以这样写: private ABCService abcService; public void setAbcService(ABCService abcService) { this.abcService = abcService; } 这当然没问题。但很多代码生成器会因此失效,因为这些工具一般都期望类名和实例名只有第一个字母的大小写不同。 所以,还不如换成AbcService,虽然这种命名中失去了“专有名词”的信息,但可以保证你写代码的流畅度: private AbcService abcService; public void setAbcService(AbcService abcService) { this.abcService = abcService; }

spring支持Bean的继承

原文: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-child-bean-definitions Bean的继承和类的继承一样,都是为了减少重复键入:父Bean里的属性可被子Bean自动继承,在写子Bean时就不用再次键入这些属性了。 当然,子Bean可以覆盖父Bean的属性 最后,父Bean还可以做成abstract的。抽象的父Bean可以不与任何java类绑定 <bean id="inheritedTestBeanWithoutClass" abstract="true"> <property name="name" value="parent"/> <property name="age" value="1"/> </bean> <bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean" parent="inheritedTestBeanWithoutClass" init-method="initialize"> <property name="name" value="override"/> <!– age will inherit the value of 1 from the parent bean definition–> </bean> 不过,子Bean并不会继承父Bean的所有特性。文档说: The remaining settings will always be taken from the child definition: depends on, autowire mode, dependency check, …

spring支持Bean的继承 Read More »

无废话:如何使用ibatis type handler  

使用ibatsi时,如果你的javabean中有个属性不是string, date, int等数据库直接支持的类型,那么就要用自定义ibatis type handler,来告诉ibatis在对它进行数据库存取时应该如何做java type与sql type的互转。 1. 建一个类实现TypeHandlerCallback接口, 如 public class SomeTypeHandlerCallback implements TypeHandlerCallback { …. } 2.在sql map文件中使用它。为了避免写起来太啰嗦,建议先定义一下typeAlias <typeAlias alias="someTypeHandlerCallback " type="com.some….SomeTypeHandlerCallback" />    3.在用于查询的resultMap中引用一下 <resultMap id="helloResult" class="hello"> … <result property="some" column="SOME" typeHandler="someTypeHandlerCallback"/> … 4.在用于增、改的parameter中也要引用一下 <insert id="insertHello" parameterClass="hello"> <![CDATA[ insert into hello ( …, SOME )values ( … #some,handler=someTypeHandlerCallback# , ); ]]> 特别注意: …

无废话:如何使用ibatis type handler   Read More »

缓存对象的数据结构要变更,怎么办?

如果缓存对象的数据结构要变更,也就是说,新的代码里将使用新的数据结构,那么上线时,线上已有缓存对象的数据结构可能跟新代码中定义的数据结构不匹配,在反序列化时可能会产生数据完整性的问题。 为了解决这个问题,就 要让新代码取不到旧缓存或者忽略旧缓存。 一个解决方案是在新代码中使用新的序列号,新代码获取旧缓存时会发生反序列化错误; 我们再显式地捕捉这种错误,在发生这种错误时再去后端取数据,并使用取来的数据覆盖旧缓存。 然而,“更新序列号”这个方案有个触不到的地方:泛型的更换。如果缓存对象的数据结构从ArrayList<A>变成了ArrayList<B>,即使A和B的序列号不同,新代码在遇到旧缓存时也不会发生序列化错误。后果是, 新代码以为它拿到的是ArrayList<B>对象,实际上却是ArrayList<A>对象,最终的后果就是在访问List中元素时发生ClassCastException. 你会问,能否侦测这种情况?比如说,新代码从缓存中拿到一个ArrayList时,判断它的泛型是不是ArrayList<B>,如果不是则忽略?  答案是做不到,java的“伪泛型”不支持这种判断。 另一个解决方案是上线前先清空所有旧缓存,这样新代码永远取不到旧缓存。然而它亦有短板: 如果你们采用了灰度发布流程,旧缓存仍然可能跟新代码接触。 所谓灰度发布是指将新代码发布到集群服务器时,先发到部分机器,针对这部分机器验证通过后再继续发下去。 显然,在这个过程中,新旧代码会同时存在于集群中;运行中的旧代码有可能会产生旧的缓存,且被新的代码获取,发生问题;同理,新代码产生的缓存也会被旧代码取到,照样发生问题。  清空旧缓存这种方案还有一个问题是,你需要在发布时手工介入,比较麻烦。 那怎么办才好呢? 有个方案是:除了使用新的序列号, 新代码也要使用新的缓存key.如果老代码使用 key_ record_id来存取,新代码就用new_key_ record_id来存取. 这样的话,新代码永远取不到老的缓存;老的代码也取不到新的缓存。 有了这个基础,泛型变换、灰度发布时的新旧代码共存都不是问题。 使用新的缓存key,可以把新旧缓存隔离开来,相当于在逻辑上产生了两套缓存空间,是一种相当彻底的解决方案; 另外,这种方案也非常简单,不需要在发布时手动做什么事情。 不过,实施这种方案有个前提:由于旧缓存会被新代码忽略,从而成为无用的缓存;新系统上线时会有大量的Cache Miss,你要确保这不会引发性能问题。