从接口解耦表示的含义(c ++)

时间:2015-03-03 10:15:40

标签: c++ terminology

我正在阅读Stroustrup用c ++编程的第2章。当他从具体类型转换为抽象类型时,他提到具体不与表示相联系。因此,如果类Stacks以显着的方式发生变化,则用户必须重新编译。

但是我没有看到Stacks是抽象的情况与他以相同方式使用派生类的情况有所不同。那么接口的解耦实际上做了什么呢?为什么在某些情况下或大多数情况下都可取?

编辑:这本书是" c ++编程语言"特别版(2000年)。第2章,第5.4页。遗憾。

1 个答案:

答案 0 :(得分:3)

接口定义了实现必须支持的逻辑操作,以允许客户端代码访问某些功能。例如,支持输出操作的抽象类型可能是:

struct Abstract_Output
{
    virtual void blocking_write(const char* p, size_t n) = 0;
};

许多不同的输出设备可以拥有满足该接口的自己的实现。例如,一个最小的低级TCP库可能会报告一旦它至少发送了一部分消息 - 告诉你写了多少字节 - 但可能不支持自动重新尝试,直到所有特定数量的字节已经传播,但需要很长时间。实现可能如下所示:

struct TCP_Output : Abstract_Output
{
    TCP_Output(const char* server_name, int port) : tcp_(server_name, port) { }

    void blocking_write(const char* p, size_t n) override
    {
        size_t bytes_written = 0;
        while (n && (bytes_written = tcp_.write(p, n)) > 0)
        {
            p += bytes_written;
            n -= bytes_written;
        }
        if (n > 0) throw std::runtime_error("incomplete TCP write");
    }
  private:
    TCP tcp_;
};

另一方面,如果您正在写入std::ostream对象,它将一直阻塞,直到写入确切的请求字节数,因此我们可以写:

struct Stream_Output : Abstract_Output
{
    Stream_Output(std::ostream& os) : os_(os) { }

    void blocking_write(const char* p, size_t n) override
    {
        os_.write(p, n_);
    }
};

然后,您可以通过抽象类/结构使用运行时多态来编写可以使用任何类型的输出对象的函数:

void report(Abstract_Output& o)
{
    std::ostringstream oss("/--- REPORT --/\n");
    for (auto& x : stocks)
        oss << x << '\n';
    o.blocking_write(oss.str().c_str(), oss.str().data());
}

然后可以通过任何实现进行调用:

Stream_Output stream_output(std::cout);
report(stream_output); // report to std::cout
TCP_Output tcp_output("localhost", 9191);
report(tcp_output);  // write report to the TCP server listening on port :9191

将以上所有内容与您的问题联系起来:

  

当他从具体类型转换为抽象类型时,他提到具体不与表示相联系。因此,如果类Stacks以显着的方式发生变化,则用户必须重新编译。

[[也请引用他的确切文字,然后我们可以看看你是否误解了它。 ]]我们通过使用抽象接口实现的目的是不将像report这样的函数耦合到具体的输出实现,如TCP_OutputStream_Output。像report这样的函数可以放在它们自己的头文件/实现文件中,如果某个客户端代码想要用不同的Abstract_Output派生的具体输出实现调用它们,则不需要重新编译。

  

但是我没有看到Stacks是抽象的情况与他以相同方式使用派生类的情况有所不同。那么接口的解耦实际上做了什么呢?为什么在某些情况下或大多数情况下都可取?

所以 - 如上所述,翻译单元只需要提供抽象类来提供report等功能。此外,可以使用report(在重新链接之后)将报告发送到甚至未设想的输出设备,更不用说在编写report函数时实现。那是脱钩。