【问题标题】:eliminate duplicate code for similar function definitions消除类似函数定义的重复代码
【发布时间】:2017-06-29 15:05:58
【问题描述】:

我有一个由一些逻辑 + 底层系统调用组成的方法。现在必须实现另一种包含完全相同逻辑但仅更改底层系统调用的方法。

我正在尝试想办法重用公共代码并实现另一种方法,该方法可能需要一个可调用对象来调用底层系统调用,但由于 readrecv 调用的空灵性而没有成功不一样。

如果能找到一个相同的优雅解决方案,那就太好了。这些方法看起来像 -


第一个函数

std::string Socket::read(const int bufSize) const
{
    auto buffer = std::make_unique<char[]>(bufSize + 1);
    auto recvd = 0, count = 0;

    std::string str;
    str.reserve(bufSize);

    do {

        // ONLY THIS PART IS DIFFERENT
        recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
        // ONLY THIS PART IS DIFFERENT

        count += recvd;
        if (count == bufSize) {
            str.append(buffer.get());
            str.reserve(str.length() + bufSize);
            std::memset(buffer.get(), 0, bufSize);
            count = 0;
        }
    } while (recvd > 0);

    str.append(buffer.get(), count);

    if (recvd == -1) {
        // TODO: Check for recvd == EAGAIN or EWOULDBLOCK and
        // don't throw exception in that case.
        throw std::runtime_error("Error occurred while writing message");
    }

    return str;
}

第二个功能

std::string Socket::recv(const int bufSize, SF::recv flags) const
{
    auto buffer = std::make_unique<char[]>(bufSize + 1);
    auto recvd = 0, count = 0;

    std::string str;
    str.reserve(bufSize);

    do {

        // ONLY THIS PART IS DIFFERENT
        const auto f = static_cast<int>(flags);
        recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);
        // ONLY THIS PART IS DIFFERENT

        count += recvd;
        if (count == bufSize) {
            str.append(buffer.get());
            str.reserve(str.length() + bufSize);
            std::memset(buffer.get(), 0, bufSize);
            count = 0;
        }
    } while (recvd > 0);

    str.append(buffer.get(), count);

    if (recvd == -1) {
        // TODO: Check for recvd == EAGAIN or EWOULDBLOCK and
        // don't throw exception in that case.
        throw std::runtime_error("Error occurred while writing message");
    }

    return str;
}

【问题讨论】:

  • 你能解释一下代码应该做什么吗?我得到了基本要点,但我认为可能有更简单和更短的方法来实现这两者。
  • 虽然我应该提一下,简单的方法是将函数逻辑简单地拆分为不同的函数。没有什么惊天动地的。
  • @tambre 从&lt;sys/socket.h&gt; socket 读取并返回std::string
  • @AbhinavGauniyal 如果您已经有工作代码,那么要求改进和重构的更好地方可能是SE Code Review
  • 推荐阅读帮助页面,在发布前询问代码审查问题,并确保您符合他们的规则,否则您可能会在这里被退回。

标签: c++ networking refactoring c++14 code-reuse


【解决方案1】:

当我要比较你的std::string Socket::read(const int bufSize) conststd::string Socket::recv(const int bufSize, SF::recv flags) const 的版本时

唯一的区别是

const auto f = static_cast<int>(flags);

recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);

因此,您的第一个版本可以使用一组特定的 flags 重构为对第二个版本的调用。

或者您可以为flags 提供一个默认值,例如

 std::string Socket::recv(const int bufSize, SF::recv flags = DefaultFlags) const

【讨论】:

    【解决方案2】:

    在 C++14 中,您可以这样做。这是一个更灵活的解决方案,尽管它的设计并不适合您的确切需求。因此,它的表现力可能会降低。

    #include <utility>
    
    namespace detail {
    
    template <class Fn, class... Args>
    auto DuplicatedCode(Fn &&fn, Args&&... args) {
      // some code
    
      // auto result =
      std::forward<Fn>(fn)(std::forward<Args>(args)...);
    
      // more code
    
      // return
    }
    
    }
    
    void foo() {
      detail::DuplicatedCode([](){return 0;});
    }
    
    void bar() {
      detail::DuplicatedCode([](){return 1;});
    }
    

    您可以在 foo 和 bar 中声明一些局部变量,DucplicatedCode 会将它们转发到 fn 或者您可以简单地捕获这些变量。

    【讨论】:

    • 这是非常好的方法恕我直言,这也是完美转发的例子吧?如果这是您的解决方案的预期用途,您介意检查 - ideone.com/WNm35N
    • 是的,完全正确。这里不需要 Lambda 表达式,你明白这一点,你就会明白整个想法:)。如果需要,您还可以修改对 fn 的调用(在 DuplicatedCode 等中传递一些局部变量。)
    【解决方案3】:

    如果读取的消息被调用,最简单的解决方案是对方法求和并传递一个无效标志:

    if(flags==INVALID)
            recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
        else
            recvd = ::recv(sockfd, buffer.get() + count, bufSize - count, f);
    

    但是,这将违反Single Responsibility Principle,因为该方法现在有两个职责和两个更改的理由。

    更好的解决方案是提取两种方法的共同部分。

    read() {
     commonMethod1();
     ::read();
     commonMethod2();
    } 
    write() {
     commonMethod1();
     ::read();
     commonMethod2();
    }
    

    但是,您的问题可能基于不同的意见,但这是我的问题。 ;-)

    【讨论】:

      【解决方案4】:

      一种简单的方法是在Socket 类中添加一个私有帮助函数

      class Socket
      {
           // everything else you already have
      
           private:
      
              std::string recv_helper(int bufSize, const SF::recv *flags = nullptr) const;
                    // note the second argument is a pointer  
      };    
      

      并将其实现为

      std::string Socket::recv_helper(int bufSize, const SF::recv *flags) const
      {
           //   All the code preceding your first // ONLY THIS PART IS DIFFERENT
      
           if (flags)
           {
               recvd = ::recv(sockfd, buffer.get() + count, bufSize - count,
                              static_cast<int>(*flags));
           }
           else
           {
               recvd = ::read(sockfd, buffer.get() + count, bufSize - count);
           }
      
            //   All the code following your second // ONLY THIS PART IS DIFFERENT    
      }
      

      那么你需要做的就是重新实现你的两个函数来调用助手。

      std::string Socket::read(int bufSize) const
      {
          return recv_helper(bufSize);
      }
      
      std::string Socket::recv(int bufSize, SF::recv flags) const
      {
          return recv_helper(bufSize,  &flags);
      }
      

      请注意,我还从按值传递的参数中删除了多余的 const 限定符。

      【讨论】:

        猜你喜欢
        • 2022-11-08
        • 1970-01-01
        • 2017-11-22
        • 1970-01-01
        • 1970-01-01
        • 2012-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多