代码之家  ›  专栏  ›  技术社区  ›  deworde

如何将代码重构为子例程,但允许提前退出?

  •  1
  • deworde  · 技术社区  · 15 年前

    在这个(有效的)代码中有一个非常明显的重构机会。

    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;
    }
    

    问题是,如果这是一个exclude语句,我就无法绕过我想尽早退出的问题。

    注意,我不能更改规则类的结构。

    有什么建议吗?

    7 回复  |  直到 15 年前
        1
  •  2
  •   Péter Török    15 年前

    显然,如果您可以迭代 ni op 成员 Rule 在一个循环中如果无法重构 规则 ,也许您可以围绕它创建一个包装器来实现这个目标。

    如果您有一个使用此类代码的单一方法,那么我就不必费心了。在我看来,只有你能用几种类似的方法消除重复的代码,这才会有回报。

        2
  •  3
  •   Nick Dandoulakis    15 年前

    下面是一个可能的重构,但我不确定它是否值得 麻烦

    #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;
    }
    
        3
  •  3
  •   Gorpik    15 年前

    我想 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;
    }
    
        4
  •  3
  •   Tomek    15 年前
    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;
    }
    

    对答案进行了编辑,仅包括最终版本。

    顺便说一句,这个问题很有趣,只要给我更多的时间,我就提出了STL算法和函数…

        5
  •  1
  •   Matthieu M.    15 年前

    您可以通过创建某种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();
    

    指南 :包装不能修改的内容!(看看适配器系列的模式)

        6
  •  0
  •   Tomek    15 年前

    这是一个基于算法的解决方案,它是核心。他们说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);
    }
    
        7
  •  0
  •   Ben Voigt    15 年前

    使用in/out参数是获得两个返回值的简单有效的方法:

    另外,我认为你需要对 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;
    }