- 主题:一个封得巨烂但被大量调用的函数,有啥妙招重构不?
两个字:巨烂
1、把成功字符串和拼凑的错误原因字符串放在同一个返回值里;
2、有两三百处调用这个函数,到处都要改调用代码;
3、无缓存层(只缓存了etag,没缓存返回的成功串);
4、http 304时,调用者还要判断一下字符串是不是“Content not modified”,此时要使用调用者自己缓存的上次的成功字符串;
5、http 404应该是给服务端擦屁股的(估计服务端在负载大时可能返回404,才那么写);
骨架大概长这样。貌似AI也没有特别令人满意的招数,难以避免修改那几百处调用的代码。
std::string HttpPost(const std::string &url, const std::string &body1, const bool use_etag) {
std::stringstream result;
try {
std::string err;
auto reqBody = EncryptMessage(body1, err, kDefaultCryptProtocol);
if (!err.empty()) {
CLOG4_DEBUG("[HttpPost] encrypt message failed due to " << err);
return "###Error### encrypt message failed";
}
...
long responseCode = curlpp::infos::ResponseCode::get(request.GetCurl());
switch (responseCode) {
case 200:
break;
case 304:
case 404:
return std::string("Content not modified");
default:
return std::string("###Error### Cloud Error Code: ") + std::to_string(responseCode);
}
} catch (curlpp::LogicError &e) {
CLOG4_DEBUG("[HttpPost] LogicError " << e.what());
return std::string("###Error###") + e.what();
} catch (curlpp::RuntimeError &e) {
CLOG4_DEBUG("[HttpPost] RuntimeError " << e.what());
return std::string("###Error###") + e.what();
}
...
std::string err;
auto plaintext = DecryptMessage(result.view(), err);
if (!err.empty()) {
CLOG4_DEBUG("[HttpPost] decrypt message failed due to " << err);
return "###Error### decrypt message failed";
}
return plaintext;
}
--
FROM 111.199.147.*
我不想在以后的新功能里再调用这个玩意儿了,哈哈
不改变已有调用者代码的办法,就是2楼dilemma说的办法,
针对老的调用者表现出接口语义不变,但改成返回一个带operator const std::string &()操作符重载的struct。新的调用者可以用这个struct里导出的新接口。
AI给的也是这个办法。
老调用者拿到混合的std::string后,
1、如果带###Error###,就认为失败,并记录失败log到文件。
2、如果是Content not modified,就需要从自己搞的缓存里拿出上次成功的串来使用。
3、否则就是成功,缓存到自己的某个地方,并使用。
其中,2不能简单和1一样以失败了之,因为有两个url接口的返回值要联合起来使用的情况,
比如:
url1返回成功(有新的策略或者配置数据),而url2返回Content not modified(策略或者配置数据没变),
然后url1和url2的响应数据要做一个关联处理(实际就是把服务端的关联计算的开销给移到客户端了)。
这种情况下url2不能把Content not modified当作失败来处理,否则会导致url1的更新也等于是失败了。
另外,我打算把成功的结果统一在HttpPost()的内部进行缓存,而无需每个调用者自己缓存。
只缓存带etag的请求。
这个缓存在多线程下是存在race从而导致miss的,比如:
线程A先请求url1,拿到成功值,
而线程B以同样的参数后请求url1,拿到的是Content not modified,但此时线程A并不一定就把成功值放入缓存了,就产生cache miss。这种先就当作失败处理好了。
如果我放弃对现有这个HttpPost()的重构,调用这个老接口的代码也不动,
而另起炉灶搞一个HttpPost2(), 只给新功能用,也会因为上面说的这个统一缓存,而需要在老接口HttpPost()里对缓存进行CRUD。
【 在 ziqin 的大作中提到: 】
: 重构的目的是什么?让代码可读性变强还是增加性能?如果是纯业务逻辑,没什么重构的必要
--
FROM 111.199.147.*
C++新手在练手时写的,但也是产品刚搭架子的阶段。这个函数、这个函数的调用者都是这位写的,现在看到的是重构过了的
他还搞了一堆随便返回C风格的返回值0、-1、-2、-3、-4、-5...的函数,
我说不要这么搞,错误码起码要用enum class,他说这是局部错误码,殊不知“局部错误码”在调用者很多、调用层级多了之后就不是“局部”的,而是灾难。还不如"都只返回true/false + log error reason string",因为错误码最终也是为了定位错误原因。
【 在 finlab 的大作中提到: 】
: 这个返回的字符串其实包含了跟调用者约定的协议。要改,只能跟调用者一起改。
: 这个函数的问题主要是把真正的返回值跟错误信息编码在一起了。
: 用个tuple,或者写个专门包装返回信息的类
: ...................
--
修改:z16166 FROM 111.199.147.*
FROM 111.199.147.*