在这个(工作)代码中有一个非常明显的重构机会。
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
if(rule.get_ni1() == *iter)
{
// If there's a match, check if it's flagged include or exclude
const std::string flag = rule.get_op1();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE"){ included = true; }
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni2() == *iter)
{
const std::string flag = rule.get_op2();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni3() == *iter)
{
const std::string flag = rule.get_op3();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni4() == *iter)
{
const std::string flag = rule.get_op4();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
if(rule.get_ni5() == *iter)
{
const std::string flag = rule.get_op5();
if(flag == "INCLUDE"){ included = true; }
else if(flag == "EXCLUDE"){ return false; }
}
}
return included;
}
我想将其转为:
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
included |= matchNICode(rule.get_ni1(), rule.get_op1);
included |= matchNICode(rule.get_ni2(), rule.get_op2);
included |= matchNICode(rule.get_ni3(), rule.get_op3);
included |= matchNICode(rule.get_ni4(), rule.get_op4);
included |= matchNICode(rule.get_ni5(), rule.get_op5);
}
return included;
}
bool Translations::matchNICode(const std::string& ni,
const std::string& op)
{
if(ni == *iter)
{
if(op == "INCLUDE"){ return true; }
else if(op == "EXCLUDE"){ /*Return worse than false*/ }
}
return false;
}
问题在于,如果它是一个排除声明,我无法解决我想提前退出的问题。
请注意,我无法更改Rule类的结构。
有什么建议吗?
答案 0 :(得分:3)
一种可能的重构如下,但我不确定是否值得麻烦
#define NI_CLAUSE(ID) \
if(rule.get_ni ## ID() == *iter) \
{ \
const std::string flag = rule.get_op ## ID(); \
if(flag == "INCLUDE"){ included = true; } \
else if(flag == "EXCLUDE"){ return false; } \
}
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
NI_CLAUSE(1)
NI_CLAUSE(2)
NI_CLAUSE(3)
NI_CLAUSE(4)
NI_CLAUSE(5)
}
return included;
}
答案 1 :(得分:3)
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
struct
{
RULE_GET_NI get_ni;
RULE_GET_OP get_op;
} method_tbl[] =
{
{ &Rule::get_ni1, &Rule::get_op1 },
{ &Rule::get_ni2, &Rule::get_op2 },
{ &Rule::get_ni3, &Rule::get_op3 },
{ &Rule::get_ni4, &Rule::get_op4 },
{ &Rule::get_ni5, &Rule::get_op5 },
};
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
for(size_t n = 0; n < 5 /* I am lazy here */; ++n)
{
if((rule.*(method_tbl[n].get_ni))() == *iter)
{
// If there's a match, check if the rule is include or exclude
const std::string flag = (rule.*(method_tbl[n].get_op))();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE"){ included = true; }
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE"){ return false; }
}
}
}
return included;
}
编辑答案只包括最终版本。
BTW这个问题很有趣,请给我一些时间,我想出了stl算法和函子......答案 2 :(得分:3)
我认为get_niX()
或get_opX()
方法有某种副作用;否则,只要你得到true
,就可以退出。
如果从matchNICode()
返回的内容确实比错误更糟糕,那么它可能是一个例外。在这种情况下,它很简单:
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
try
{
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
included |= matchNICode(rule.get_ni1(), rule.get_op1);
included |= matchNICode(rule.get_ni2(), rule.get_op2);
included |= matchNICode(rule.get_ni3(), rule.get_op3);
included |= matchNICode(rule.get_ni4(), rule.get_op4);
included |= matchNICode(rule.get_ni5(), rule.get_op5);
}
return included;
}
catch (WorseThanFalseException& ex)
{
return false; // Or whatever you have to do and return
}
}
bool Translations::matchNICode(const std::string& ni,
const std::string& op)
{
if(ni == *iter)
{
if(op == "INCLUDE"){ return true; }
else if(op == "EXCLUDE"){ throw WorseThanFalseException(); } // Whatever this is
}
return false;
}
答案 3 :(得分:2)
如果您可以循环遍历ni
op
成员Rule
和Rule
成员,那么代码显然会更清晰,更简单。如果你不能重构{{1}},也许你可以创建一个包装器来实现这个目标。
如果你有一个这样的代码方法,我不会打扰。如果你能用几种类似的方法消除重复的代码,IMO只会付出代价。
答案 4 :(得分:1)
你可以通过创建某种tribool类并使用延迟评估来解决这个问题。
class TriState
{
public:
TriState(): mState(KO) {}
bool isValid() const { return mState != FATAL; }
bool ok() const { return mState == OK; }
void update(std::string const& value,
std::string const& reference,
std::string const& action)
{
if (mState == FATAL) return;
if (value == reference)
{
if (action == "INCLUDE") mState = OK;
else if (action == "EXCLUDE") mState = FATAL;
}
}
private:
typedef enum { OK, KO, FATAL } State_t;
State_t mState;
};
然后你可以这样使用循环:
TriState state;
for (const_iterator it = nicodes.begin(), end = nicodes.end();
it != end && state.isValid(); ++it)
{
state.update(*it, rule.get_ni1(), rule.get_op1);
state.update(*it, rule.get_ni2(), rule.get_op2);
state.update(*it, rule.get_ni3(), rule.get_op3);
state.update(*it, rule.get_ni4(), rule.get_op4);
state.update(*it, rule.get_ni5(), rule.get_op5);
}
return state.ok();
现在,如果对规则的操作有某些应该避免的副作用,那么使用包装器来进行延迟评估。
class Wrapper
{
public:
Wrapper(Rule const& rule): mRule(rule) {}
std::string const& get_ni(size_t i) const { switch(i) { ... } }
std::string const& get_action(size_t i) const { switch(i) { ... } }
private:
Rule const& mRule;
};
重构update
:
void update(std::string const& value, Wrapper wrapper, size_t index)
{
if (mState == FATAL) return;
if (value == wrapper.get_ni(index))
{
if (wrapper.get_action(index) == "INCLUDE") mState = OK;
else if (wrapper.get_action(index) == "EXCLUDE") mState = FATAL;
}
}
使用双循环:
TriState state;
Wrapper wrapper(rule);
for (const_iterator it = nicodes.begin(), end = nicodes.end();
it != end && state.isValid(); ++it)
{
for (size_t index = 1; index != 6 && state.isValid(); ++index)
state.update(*it, wrapper, index);
}
return state.ok();
指南:包装您无法修改的内容! (查看适配器系列模式)
答案 5 :(得分:0)
这是承诺基于算法的解决方案,它是硬核......他们说STL是为了简化我们的程序(这比我提出的其他解决方案更长)。
struct FUNCTOR : std::unary_function<bool, std::string const &>
{
public:
FUNCTOR(Rule const &r) : included(false), rule(r)
{
}
// returns true if exluded
bool operator()(std::string const &s)
{
static METHODS methods[] =
{
{ &Rule::get_ni1, &Rule::get_op1 },
{ &Rule::get_ni2, &Rule::get_op2 },
{ &Rule::get_ni3, &Rule::get_op3 },
{ &Rule::get_ni4, &Rule::get_op4 },
{ &Rule::get_ni5, &Rule::get_op5 },
};
return(std::find_if(&methods[0], &methods[5], FUNCTOR2(rule, s, included)) != &methods[5]);
}
operator bool()
{
return(included);
}
private:
struct METHODS
{
std::string (Rule::*get_ni)() const;
std::string (Rule::*get_op)() const;
};
struct FUNCTOR2 : std::unary_function<bool, METHODS>
{
public:
FUNCTOR2(Rule const &r, std::string const &s, bool &incl) : rule(r), str(s), included(incl)
{
}
// return true if exluded
bool operator()(METHODS m)
{
if((rule.*m.get_ni)() == str)
{
// If there's a match, check if the rule is include or exclude
const std::string flag = (rule.*m.get_op)();
// If include, code is included unless a later rule excludes it
if(flag == "INCLUDE")
included = true;
// If exclude, code is specifically excluded
else if(flag == "EXCLUDE")
{
included = false;
return(true);
}
}
return(false);
}
private:
Rule const &rule;
std::string const &str;
bool &included;
};
Rule const &rule;
bool included;
};
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
FUNCTOR f(rule);
std::find_if(nicodes.begin(), nicodes.end(), f);
return(f);
}
答案 6 :(得分:0)
使用输入/输出参数是获得两个返回值的简单而有效的方法:
另外我认为你需要对rule.get_opN()
进行懒惰评估?要做到这一点,你需要使用指向成员的指针函数。
bool Translations::compatibleNICodes(const Rule& rule,
const std::vector<std::string>& nicodes)
{
bool included = false;
// Loop through the ni codes.
for(std::vector<std::string>::const_iterator iter = nicodes.begin();
iter != nicodes.end();
++iter)
{
// Match against the ni codes of the rule
if (!matchNICode(rule.get_ni1(), rule, &Rule::get_op1, included)) return false;
if (!matchNICode(rule.get_ni2(), rule, &Rule::get_op2, included)) return false;
if (!matchNICode(rule.get_ni3(), rule, &Rule::get_op3, included)) return false;
if (!matchNICode(rule.get_ni4(), rule, &Rule::get_op4, included)) return false;
if (!matchNICode(rule.get_ni5(), rule, &Rule::get_op5, included)) return false;
}
return included;
}
bool Translations::matchNICode(const std::string& ni, const Rule& rule,
std::string (Rule::* const opfn)(), bool& included)
{
if (ni == *iter) {
const std::string& op = (rule.*opfn)();
if (op == "INCLUDE") {
included = true;
}
else if (op == "EXCLUDE") {
return false;
}
}
return true;
}