我们服务分层时可能会遇到一些问题:
- 比如在componentA里我们可能会查一下用户信息,同时componentB里可能也需要用到同一个用户信息,这时我们有几种选择:
- componentA里查一下db或rpc,componentB里再查一下db或rpc。这样的话会带来多次db或rpc查询,浪费了资源,带来性能损失
- 在componentA和componentB外层查一次db,然后再传给两个component。这样问题是我们每次遇到这种情况都要改代码,把查询不断往上层提,除非提到最上层,否则也不能从根本上解决,而如果全提到最上层的话那分层意义就会弱很多
- 其中一个component查完自己存起来,然后一层层往下传,哪里用到就通过参数传过去。这样一是会带来参数无限扩张;二是有顺序问题,比如现在是A请求了存起来B用,那就要求A要在B之前调用,如果A前面再来个C也要用,那就需要把A里请求的代码挪过去,很难维护
- 很多时候,我们的方法常会产生一些逻辑过程的中间值,用于后面其它逻辑使用,这样的话中间值就会在逻辑里各种传递,可能会越传越深,导致如果这个值有什么变动,就需要改很多地方
- request带进来的参数可能需要一些预计算才可以被正确识别,比如参数uid可能需要解密或需要A、B两个参数一起才能判断是iOS还是Android,我们一般怎么处理?:
- 解密uid或通过A、B算出iOS,然后结果和request并列一起做为参数一层层传下去,但这样会使参数量膨胀
- 起一个公共方法或特殊类型的成员函数,每次使用重新计算一遍
- 手动调整request结构,在request里加个参数,把想要的结果算出来后存进去,和request一起带下去,但这样会修改原始request结构体,可能会给非修改人困惑
比如正常代码可能是下面这样:
public action1(req *request) {
bIOS = getIsIOS(req);
userInfo = getBaseUserInfo(req->uid);
expSwitch = getCurExpSwitch(userInfo);
ret1 = logic_one(req, bIOS, userInfo, expSwitch);
ret2 = logic_two(req, userInfo, expSwitch);
output(ret1, ret2);
}
public logic_one(req, bIOS, userInfo, expSwitch) {
if bIOS {
dealUser(userInfo);
}
if expSwitch->allow() {
expCode();
}
processLogic(userInfo);
}
public logic_two(req, userInfo, expSwith) {
processUserInfo(userInfo);
if expSwitch->allow() {
expProcess();
}
}
那如果解决这样的问题?
这里提出一个runtime object的东西,我理解它要起到一些作用:
- 阻止参数无限扩展:可以将所有可能参数以接口形式收敛到一个runtime object中
- 封装与数据打交道的rpc和db查询,所有数据以get形式存在并以类单例形式减少请求量
- 封装与参数有关的运算逻辑,同样以get形式存在,与用rpc和db一样,让用户只感觉获取数据,不需要关心数据来源是什么,rpc还是逻辑计算是一样的
class ExampleRuntimeObj {
private req *Req;
private userInfo *UserInfo;
public getUserInfo() {
if this->userInfo != nil {
return this->userInfo;
}
userInfo = db.FindUserInfo(this->req->uid);
this->userInfo = userInfo;
return this->userInfo;
}
public isIOS() {
if this->req->channel == "ios" ||
this->req->from == "ios" {
return true;
}
return false;
}
public isHitSwitch() {
userInfo = getUserInfo();
return getRandmonHit(userInfo->uid);
}
}
这里我们可以把runtime object当做所有获取数据的逻辑全部封装进去,在各logic之前的传递只传它做为参数就可以,其它包括数据方法,数据请求顺序和缓存全不需要计算逻辑关心
public action1(req *request) {
rtObj = newAction1RuntimeObj(req)
ret1 = logic_one(rtObj);
ret2 = logic_two(rtObj);
output(ret1, ret2);
}
public logic_one(rtObj) {
if rtObj->isIOS() {
dealUser(userInfo);
}
if rtObj->isHitSwitch() {
expCode();
}
processLogic(rtObj->getUserInfo());
}
public logic_two(rtObj) {
processUserInfo(rtObj->getUserInfo());
if rtObj->isHitSwitch() {
expProcess();
}
}
待解决的点:
- runtime object需要抽象出base类,因为像getUserInfo这类不止是一个接口会用到
- req如何抽象??runtime object里的方法是需要从req里获取参数的,在req不同的情况下,如何使所有req兼容同一个getUserInfo接口?req是否本身也要接口化??
- 很多时候我们不只需要get数据,也需要set数据,A里set的数据,是否应该通过runtime object来暂存让B看到?如果这样的话如何解决顺序问题??