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

Meaning of decoupling representation from interface (c++)

我正在阅读 Stroustrup 编写的 c++ 编程的第 2 章。当他从具体类型过渡到抽象类型时,他提到具体与表示没有耦合。因此,如果 class 堆栈发生重大变化,用户必须重新编译。

但我看不出 Stacks 是抽象的情况有什么不同,他以相同的方式使用派生的 classes。那么接口解耦到底有什么作用呢?为什么在某些或大多数情况下需要它?

编辑:本书是 "the c++ programming language" 特别版 (2000)。第 2 章,p5.4。对不起。

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

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

然后您可以编写可以处理任何类型输出对象的函数,通过抽象使用运行时多态性 class/struct:

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

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

When he makes the transition from concrete types to abstract types he mentions the concrete is not coupled to the representation. So if the class Stacks changes in a significant way the user has to recompile.

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

But I don't see the difference with the case where Stacks is abstract and he uses the derived classes in the same way. So what does decoupling of the interface actually do? Why is it desirable in some or most cases?

所以-如上所述,提供report等功能的翻译单元只需要看到摘要class。此外,report 可用于(在重新链接后)将报告发送到输出设备,这在编写 report 函数时甚至没有想到,更不用说实现了。这就是脱钩。