06月 3rd, 2008 | by 邓芝 |

服务化的行军中:Enum使用,快乐的痛苦

JDK1.5之前,一直使用类似apache commons lang中提供的Enum。

由于如下几个原因,换选了java Enum。

  1. 远程服务在序列化旧式的Enum时,默认是不支持的,需要单独进行定制。常用的WS,Hessian,XML绑定工具,都不能很平滑的支持,且时不时有些莫名其妙的故障。
  2. 在高并发的时候,存在性能瓶颈。这也是业务量上去之后,才发现的。
  3. 这个旧式的枚举在对象序列化的时候,有些偏大(同样的常量字段:350字节左右,java Enum 大概124字节左右–其实也不小呀),和其承担的角色不像匹配。
  4. JDK1.5提供的枚举,简洁,方便,序列化工具基本都支持。

新老枚举使用在了如下场景:

  • 业务常量
  • 错误代码

其实使用的场景还是很简单的,开始我们使用的很开心,很舒服,到后来,却有些苦涩了。

  1. 在我们SOA化的进程中,核心业务单元都独立起来了,核心业务采用服务-组件模式(聚合模式),产品层调用服务层,完成一次业务行为。在这个过程中,每个组件单元都有独立的业务逻辑,相互不干扰,因此就产生了不同的业务常量Enum,和错误代码Enum。后来发现,部分业务常量Enum的部分语义有差异,导致一个业务逻辑段内出现2种业务定义;最头痛的是错误代码,无法直接给产品层返回,如果返回字符串,在反转的时候,存在不清楚枚举类型的问题。
  2. 有些业务单元吸取了前次的教训,把所有的错误代码定义在了一个枚举中,用起来好似不错。唯一不爽的是:wsdl文件,居然把所有的枚举都给列出来。
  3. 在服务化不断进展中,出现了一个核心业务调用其它几个核心业务,这最外面的服务痛苦不堪,它的处境与情况1相似。
  4. 由于某个偶然的原因,还出现了A程序用b1,b2,b3枚举来详细区分业务,B程序用b来区分业务,产生了b={b1,b2,b3}的业务语境,由此出现了二维枚举定义法。
  5. 老的枚举实在太多了,只好保留,产品层采用枚举转化化。最头痛的是:核心业务重新梳理之后,增加了许多常量和错误代码,产生了不一致的情况。

在使用过程中,WEB层又提出了一些特殊要求:下拉框,复选框,都需要提供复杂的枚举,例如{name,value,msg,sequence},实际使用的复杂度,超出了原始的常量要求。这些枚举就在我们的SOA环境中生存了下来,Happy的传来传去,有些随意的在多个系统之间溜达。

一直梦想JDK的Enum类能够提供如下方法,最后,只好自己搞一个了:

public static <T extends Enum<T>> T getEnumByName(Class<T> cls, String name);

public static <T extends Enum<T>> T getEnumByValue(Class<T> cls, String value);

public static <T extends Enum<T>> List<T> getEnumList(Class<T> clazz);

public static <T extends Enum<T>> List<String> getEnumNameList(Class<T> clazz);

public static <T extends Enum<T>> Map<String, T> getEnumMapKeyIsName(Class<T> clazz);

这个过程中有好些不合理的地方,仍需解决:

  • 大规模SOA化业务过程中,业务常量的定义,使用Enum,是否是一个好的选择。好处:程序员比较容易找到;常量比较清晰,不容易出错;管理起来方便;变化周期较长,系统也允许重新发布;对分析、设计人员的要求相对较低,程序员可以方便的增加,变更业务常量。痛处:上面提到了一些,特别是最外围的聚合业务,有些不爽,产品层最是不爽。
  • Enum作为服务接口参数,在跨越系统边界的时候,变相增加了系统的耦合度。
  • Enum在域对象中适用,该域对象作为服务接口参数传递,在某些序列化方式下,会出现错误。特别是xfire的序列化,支持的很不好,如果域对象中还用Map<Enum,T>之类的,也会出现错误。此外,在open的服务接口中,可能出现其它语言不支持Enum概念的情况,因此在服务接口处,尽可能用原子类型;不爽的是,枚举的某些限制作用就会丧失,在程序内部可能出现大量比较、反转的情况。
  • 错误代码,定义为枚举,不爽,但好处和第一条一样。比较HTTP协议,信用卡标准等,可见我们在业务协议标准化层面还有得发展,如果内部业务之间的协议标准化了,那么我们的SOA,可以进行的更好,我们的产品经理,甚至可以通过这些常量,代码,协议的维护,实现对产品的可控性。
  • 二维枚举要避免。这其实也是业务没有能够标准化,一不小心就产生的变异。特别是第一个版本的业务设计,对后续的影响很是深远,从另一个层面表现出来的是:数据完整性发生了变化。可喜的是,说明了业务发展飞快;可忧的是,业务架构越来越要求具有前瞻性,扩展性,否则大家都很痛苦。
  • 业务常量,大都具有很强的业务语义。定义的准确性,合理性,对后期的维护有很大的影响。跨业务功能单元的局部重叠的业务常量,表现起来(用Enum定义)痛苦,使用起来也痛苦。如何使好呀?
  • 新老枚举共存为后续的痛苦,埋下了伏笔。
  • 一旦服务开放了,又可能存在一层映射。
  • 不用Enum,尝试某几个常量不用Enum,开始用起来很爽,测试维护的时候,傻了,这个常量是1,2,3;大于10个地方用到了,后来发现一个BUG,不能确定常量的业务语义了。特别是其它人阅读代码的时候,一头雾水。只好重新用Enum来定义,无论理解,维护都方便了。
  • 规范化枚举的格式,开始定义枚举,有多种方式,直接定义:EnumName、带转义值定义:EnumName(value)、带文本说明的定义:EnumName(value,message)。各自都很不相同,后来有做管理平台的哥们用的时候,发现很不爽,想写个Utils都不行,还不能满足要求。最后同意格式:EnumName(value,message,扩展值),EnumName表征业务,value是实际数据值(如果本身就和EnumName相等,那么也要写之),message是说明文字,枚举构造器后面的参数业务自己定义。这里面,最怪异的是message,为了方面,直接用中文了,而没有用resourceBundle之类的技术,留待以后解决之。
  1. 4 Responses to “服务化的行军中:Enum使用,快乐的痛苦”

  2. By liang on Jun 4, 2008 | Reply

  3. By 庞统 on Jun 10, 2008 | Reply

    顶,

  1. 2 Trackback(s)

  2. Jun 19, 2008: ESB zone » SOA架构之性能解决策略之一【引入Cache过程的思考点】
  3. Jun 29, 2008: ESB zone » Java Enum小心使用,它可能增加系统耦合性

Post a Comment