0

我有以下课程:

class Stream {};

class FileStream : public Stream {};

class NetworkStream : public Stream {};

每个类都有一个write()方法(虚拟)。

我可以在两种类型的媒体上使用 FileStream:HDD 和 SSD。如果我正在写入 HDD,我不会对FileStream已经提供的内容做任何特别的事情。但是,如果我正在编写 SSD,则在调用之前需要执行一些逻辑write()。从内部FileStream,我不知道我正在写信给什么媒体。只有呼叫站点知道。我想在这里使用装饰器,但装饰器旨在用于所有流。我只想NetworkStream在某些情况下扩展 的功能。某种形式的装饰器在这里合适吗?如果不是,我应该使用什么设计模式?如果我们假设FileStream::write()简单地将整个内部缓冲区刷新到文件并将其保存到磁盘,那么装饰器将需要在写入之前将一些元数据写入流。

我想创建一个不使用继承的简单装饰器类,如下所示:

class FileStreamDecorator
{
public:
  FileStreamDecorator( FileStream& stream ) : m_stream( stream )  {}

  void write() {
    m_stream << "Some Metadata";
    m_stream.write();
  }

private:
  FileStream& m_stream;
};

它会像这样使用:

FileStream stream;
stream << "Complete file data";

// At this point we know we are writing to SSD, so we must use the decorator
FileStreamDecorator decorator( stream );
decorator.write();

这会是一个合适的解决方案吗?谁能想到更好的方法?

4

1 回答 1

1

这会是一个合适的解决方案吗?谁能想到更好的方法?

不,您正在创建一个装饰器类并依赖客户端代码在需要时不要忘记使用它。

如果客户端代码忘记执行额外的步骤,代码看起来没问题(客户端代码中没有任何内容表明应该在那里初始化装饰器)。

在一个月(或五年)内,您将忘记这一点,或者转移到其他项目,并且维护该项目的人将不得不意识到需要在客户端中初始化一个新对象。

最好使用 SSDFileStream 特化,它覆盖基类中的 write()(默认行为),在内部调用基类版本,然后执行任何额外的步骤。

我能想到的最佳实现:

class FileStream {
    virtual void write();
};

class SSDFileStream: public FileStream {
    virtual void write() {
        FileStream::write();
        write_ssd();
    }
protected:
    void write_ssd();
};

此外,您可以使 FileStream 抽象,并添加 HDDFileStream 专业化。如果 HDDFileStream 检测到它正在写入 SSD,它可能会引发异常。如果您要求 SSDFileStream 在 HDD 路径上写入,它可能也可以这样做。

这将使客户端代码易于正确编写,并且不可能'*编写不正确。


'* 编写错误/不稳定/脆弱/丑陋的代码从来都不是不可能的,但你仍然可以让它难以实现。

于 2013-04-24T15:22:54.493 回答