技术开发 频道

数据校验器架构模式组

  可组装校验器

  针对第二类场景,我们对每一个数据类提供一个独立的校验规则类,因为这个数据类本身已经包含了语法和语义逻辑。语法逻辑是与数据结构相关的,在我们的示例中,是判断对象是否是UML的Class实例。而语义逻辑是与业务规则相关的,每一个数据类关联的业务规则不尽相同,可能来自不同领域,或不同的业务组件或系统;另外由于业务规则的易变性较强,可扩展性和可配置性要求也较高,所以有必要为每一个数据类设置专属的校验类。这里我们将每一个校验类称作一条校验规则(Rule)。校验规则类的接口和实现代码如下:

  清单2: IVRule.java

1 public interface IVRule {
2
3   /**
4
5   * validate value by domain rule.
6
7   */
8
9   public boolean isValid(Object value);
10
11   /**
12
13   * validate value by domain rule.
14
15   */
16
17   public void validate(Object value) throws DataValidationException;
18
19   }
20
21

  清单3: ServiceVRule.java

1 public class ServiceVRule implements IVRule {
2
3   private static String STEREOTYPE_NAME = "Service";
4
5   ………..
6
7   public void validate(Object value) throws DataValidationException {
8
9   if (value instanceof Class) {
10
11   Stereotype st = ((Class) value).getStereotype();
12
13   String name = st.getName();
14
15   if (STEREOTYPE_NAME.equals(name)) {
16
17   String wsdl = (String) st.getProperty("WSDL");
18
19   if ((wsdl == null) || (wsdl.equals("")))
20
21   throw new DataValidationException("No WSDL file is defined.");
22
23   |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
24
25   |-------- XML error: The previous line is longer than the max of 90 characters ---------|
26
27   } else
28
29   throw new DataValidationException("It is not a Service Object.");
30
31   |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
32
33   |-------- XML error: The previous line is longer than the max of 90 characters ---------|
34
35   } else
36
37   throw new DataValidationException("It is not a UML Class model.");
38
39   }}
40
41

  在这里,我们还是提供了两种校验结果返回机制,boolean 值和抛出异常,在具体应用时大家可以选择一个即可。这里的校验规则实现是关于 Web Service 的,它的语法校验是检查数据是否是 UML 的 Class 对象,而语义校验是检查 Stereotype 是否是 Service,并检查 Stereotype 中是否含有 WSDL(Web service description language) 属性。

  接下来,怎么应用这些校验规则 rule 呢?一个系统中会有很多校验规则类,那么就需要一个管理机制来管理这些校验类,最简单的我们只需定义一个方法 validate(Object value, IVRule rule),提供一层简单的封装,它可以起到一个代理的作用,比如,应用 Proxy 模式,我们可以对校验规则本身做一些安全性认证方面的工作,然后才决定是否可以用该校验规则。而更完善的管理机制是提供一个更灵活的环境,让用户可以动态组装,改变,查找校验规则类。基本思路是:我们将校验规则类当作一种可重用资源,提供一个组装工厂环境,用户可以将校验规则 rule 注册到工厂里,工厂会实例化和缓存这些类,并提供查找服务;然后提供一个校验器来从工厂里查找出相应的校验规则 rule 类,为用户提供校验服务。这里面用到了 Factory,Flyweight,Registry 模式。(本文引用的模式请参考相关模式)

  第一步,我们设计一个组装工厂类,它提供实例化、缓存、和查找服务,很显然,实例化类是一个 Factory 模式的基本职责,将实例缓存 cache 是一个 Flyweight 模式的基本职责,查找服务可以很简单,在我们的例子中就是从一个 Map 中取出校验规则 rule 实例,也可以复杂化,比如我们的校验规则类是一个远程资源,或者是实例化这个类需要用到其它的远程资源,如数据库,那这个查找功能实现起来可能就复杂些,可以通过 JNDI 来查找,也可以将远程资源暴露成服务(Web Service)并注册到 UDDI (Universal Discover Description and Integration),然后从 UDDI 中查找服务。从这里我们可以看到这个组装工厂类具备管理校验规则 rule 类的整个生命周期的职责,这为校验器应用提供了很大的灵活性和可扩展性,假如我们今后需要实现一个实例池或资源池 Pool 来管理这些校验规则实例,那么只需要将组装工厂类的功能稍作修改和扩展就可,而不必触及校验器应用的其他类,因为我们已经将实例的管理逻辑从整个校验器应用中剥离出来,管理职责的变化只局限在组装工厂类内,对别的类是封闭的,这正体现面向对象的基本设计原则之一 Open-Close 原则(对修改封闭,对扩展开放)。组装工厂类的代码如下:

  清单4: VRuleAssemblerFactory.java

1  public class VRuleAssemblerFactory {
2
3   private static Map rules = new HashMap();
4
5   /**
6
7   * 查找校验规则Rule。
8
9   */
10
11   public static IVRule lookupVRule(String ruleHandler) {
12
13   if (ruleHandler == null)
14
15   return null;
16
17   IVRule rule = null;
18
19   if (rules.containsKey(ruleHandler))
20
21   rule = (IVRule) rules.get(ruleHandler);
22
23   return rule;
24
25   }
26
27   /**
28
29   * 注册/加入一个校验规则Rule.
30
31   */
32
33   public static void addVRule(String ruleHandler, Class ruleClass) {
34
35   if ((ruleHandler != null) && (ruleClass != null)) {
36
37   try {
38
39   rules.put(ruleHandler, ruleClass.newInstance());
40
41   } catch (InstantiationException e) {
42
43   e.printStackTrace();
44
45   } catch (IllegalAccessException e) {
46
47   e.printStackTrace();
48
49   }
50
51   }
52
53   }
54
55   /**
56
57   * 批量载入校验规则Rule,一般在应用系统初始化时调用。
58
59   */
60
61   public static void assembleVRules() {
62
63   addVRule("Table", TableVRule.class);
64
65   addVRule("Service", ServiceVRule.class);
66
67   }
68
69   }
70
71

  从上面的代码可以看到,我们用一个 Map 作为缓存库,注册一个校验规则时以字符串作为关键字,当然也可以用别的自定义类型,在查询时利用了 Map 的查找功能很简单高效地实现了查找功能,另外我们还定义了一个批量载入校验规则的方法,这个功能是为了用户使用方便,在应用系统初始化时执行一次,而且可以透明地载入校验规则,在本例中只是硬编码了这些校验规则类,需要的话我们可以从别的元数据文件中(XML 或 CSV 文件)导入。

  还需注意一点的是,这个组装工厂是一个全局类,在这里是以静态static方式实现的,当然也可以以Singleton方式来实现,还可以以线程安全ThreadLocal的方式来实现。

  第二步,我们设计校验器类,校验器类应该作为整个校验器应用的 Façade,用户需要校验数据时只需和它打交道,这就很好的把校验规则类隐藏了起来,因此校验器的职责有查找相应的校验规则类和执行校验。校验器类的接口和实现代码如下:

  清单5: IValidator.java

1  public interface IValidator {
2
3   /**
4
5   * 通过关键字ruleHandler查询校验规则来校验数据。
6
7   */
8
9   public boolean isValid(Object value, String ruleHandler);
10
11   /**
12
13   *通过关键字ruleHandler查询校验规则来校验数据。返回异常。
14
15   */
16
17   public void validate(Object value, String ruleHandler)
18
19   throws DataValidationException;
20
21   /**
22
23   * 直接指定校验规则类来校验数据。
24
25   */
26
27   public boolean isValid(Object value, IVRule rule);
28
29   /**
30
31   *直接指定校验规则类来校验数据,返回异常。
32
33   */
34
35   public void validate(Object value, IVRule rule) throws Exception;
36
37   }
38
39

  清单6: AssemblyValidator.java

1  public class AssemblyValidator implements IValidator {
2
3   private static AssemblyValidator instance = null;
4
5   private AssemblyValidator() { }
6
7   public static synchronized AssemblyValidator getInstance() {
8
9   if (instance == null)
10
11   instance = new AssemblyValidator();
12
13   return instance;
14
15   }
16
17   public boolean isValid(Object value, String ruleHandler) {
18
19   boolean valid = false;
20
21   IVRule rule = VRuleAssemblerFactory.lookupVRule(ruleHandler);
22
23   if (rule != null)
24
25   valid = rule.isValid(value);
26
27   return valid;
28
29   }
30
31   public void validate(Object value, String ruleHandler)
32
33   throws DataValidationException {
34
35   IVRule rule = VRuleAssemblerFactory.lookupVRule(ruleHandler);
36
37   if (rule != null)
38
39   rule.validate(value);
40
41   }
42
43   public boolean isValid(Object value, IVRule rule) {
44
45   boolean valid = false;
46
47   if (rule != null)
48
49   valid = rule.isValid(value);
50
51   return valid;
52
53   }
54
55   public void validate(Object value, IVRule rule) throws Exception {
56
57   if (rule != null)
58
59   rule.validate(value);
60
61   }
62
63   }
64
65

  从上面代码可以看到,我们使用了Singleton模式,因为没必要每次都实例化校验器类。另外我们在这里提供了两套返回机制,还有两套取校验规则的方式,这都可以根据实际应用作出取舍。

  可组装校验器的架构图如下:

  图 1:可组装校验器的架构图

0
相关文章