今天晚上和项目组的几个同学讨论一个服务方法的入参设计,觉得挺有意思的,在此记录一下!
背景
我们的场景需要对外暴露一个远程服务写入接口,用于给几个外部同步数据的场景。
由于历史的原因,需要被写入的这个业务对象很大,字段很多(100多个字段,也会涉及多个表)。
以下是场景提取出来的一些要求:
1.这个场景只需要同步更新几个字段到该主表里。为了数据安全和方便理解,不希望把整个业务对象暴露出来,希望只暴露几个该场景可以写入的字段。
2.外部场景都是能拿到该业务对象的唯一外键的,所以为了交互友好,不希望流程是"远程读取数据-->修改数据-->远程更新数据",而希望是"拼装需要同步的数据-->远程同步"的流程。
注:用的是公司内部应用的的java远程调用服务框架,只在java语言内部调用。只考虑java 语言层面的设计,不考虑soap这些重量级的方案。
入参的设计方案
方案一
一开始想得是设计一个只包含可更新字段的pojo model类作为入参。如:
public class WritableModel {
String property1;
String peoperty2;
public String getProperty1() {
return property1;
}
public void setProperty1(String property1) {
this.property1 = property1;
}
public String getPeoperty2() {
return peoperty2;
}
public void setPeoperty2(String peoperty2) {
this.peoperty2 = peoperty2;
}
}
这个model强类型固然好,但是有一个很难避免的问题就是二义性。
在property1为null的时候,是说明不更新这个字段还是要清空这个字段呢?(注意:在这里的写场景里这两种情况都需要支持!)
强类型虽然约束了可更新范围,这个时候pojo里的null是没办法去理解。
方案二
由于方案一的缺陷根本就没办法满足需求,于是想了下用map来作为入参,但是map是弱类型,我们必须定义一些key,并让调用方只用这个key。于是想到了将key用一个枚举来表示,然后入参用java map泛型来约束.
如下:
key的枚举类设计如下:
public enum WritablePropertyType {
PROPERTY1_NAME//属性1的名字
, PROPERTY2_NAME//属性2的名字
}
服务方法入参设计如下:
/**
*
* @param map 需要被同步的字段
* @return 是否成功
*/
public boolean sync(Map<WritablePropertyType,Object> map);
以上虽然很简洁,对于调用者只需要将自己需要更新的字段拼装成map,同时key使用WritablePropertyType 泛型约束。如果需要对某个字段清空,只需要put一个null值进来就行了。
但是泛型约束并不那么强,对于不遵循泛型的map的入参虽会有警告但是没办法做到强类型的约束,还是存在一些风险。难道在服务端进行key类型检查,不规范的抛异常吗?为了更好的约束,又有了方案三。
方案三
方案二随能很好满足需求,但是泛型毕竟约束是不够的,于是想了方案三。基本是延续方案二的思路,用map。也是由自己设计的WritablePropertyType 枚举作为map的key。但是要对map做一些改造,使得map只能放进来以WritablePropertyType作为key的map。于是想到对HashMap做一个扩展,只是简单的复写put方法,并在put方法里强制检查。
复写的map如下:
import java.util.HashMap;
public class WritableMap<K, V> extends HashMap<K, V> {
/**
*
*/
private static final long serialVersionUID = 1L;
public V put(K key, V value) {
if (key instanceof WritablePropertyType) {//强制检查
super.put(key, value);
return value;
} else {
throw new IllegalArgumentException(
"key type must be WritablePropertyType");
}
}
}
服务方法入参设计如下:
/**
*
* @param map 需要被同步的字段
* @return 是否成功
*/
public boolean sync(WritableMap<WritablePropertyType,Object> map);
以上方案三是最后定下来一个折中的方案,相信这还不是最优方案,不知道大家有没有更好的方案?说说你的观点吧,:)