4

我有一个不断遇到的设计问题。

为了说明起见,假设我有一个多态类层次结构

class A { public: virtual ~A() {} ... };
class B: public A { ... };
class C: public B { ... };
class D: public A { ... };
...

我希望能够以多态方式打印这些类的实例,即每个类都有自己的打印方式。实现这一目标的明显方法是添加

virtual void print(OutputStream &os) = 0;

进入基类并在每个子类中覆盖此方法。但是,如果类的原始职责与打印无关,这将为它们增加另一个职责,从而违反SRP

我的问题是:在不违反 SRP 的情况下实现所需行为的正确方法是什么?

这篇文章中,提出了一种基于访问者设计模式的解决方案。但是,然后我需要创建一个必须知道每个子类的类A。我希望能够添加和删除子类,而无需总是修改访问者。

除了上述两种方法之外,还有其他一些保留 SRP 的方法吗?

4

4 回答 4

3

有一种非循环访问者模式,无需了解每个子类。它依赖于dynamic_cast,但可能是您需要的。

于 2013-07-25T06:43:35.733 回答
3

类打印本身没有任何问题。它不违反 SRP,因为打印不构成责任。

请记住,责任被定义为改变的理由。您不会更改课程,因为您对打印的要求发生了变化。该类应该只将名称-值对发送到负责打印的实体,称为格式化程序。这个发送名称-值对的过程永远不会自行改变。其中的任何更改仅由与打印无关的其他更改提示(例如,当您添加一个字段时,您也将其表示添加到打印过程中)。

格式化程序应该对它打印的类一无所知,而只是根据一些要求呈现名称-值对。当打印要求发生变化时,格式化程序也会发生变化。因此,打印将是格式化程序的唯一责任。

于 2013-07-25T16:09:52.570 回答
1

为了做到这一点,您需要寻求某种双重调度解决方案的访客。双分派方法更轻量级,所以这样的事情怎么样:

在一个:

class Processor
{
public:
  virtual void Process(const A &a)const {}
  virtual void Process(const B &b)const {}
  virtual void Process(const C &c)const {}
  virtual void Process(const D &d)const {}
  virtual void Process(const E &e)const {}
};

在一个:

class A
{
public:
  virtual void Process(const Processor &processor) 
  {
    processor.Process(*this);
  }
};

然后,在每个派生类Process中使用相同的定义覆盖:

virtual void Process(const Processor &processor) 
{
  processor.Process(*this);
}

这将确保Process调用正确的重载。

现在,创建一个流处理器:

class StreamProcessor : public Processor
{
private:
 OutputStream &m_OS;

public:
  StreamProcessor(OutputStream &os) : m_OS(os)
  {
  }

  virtual void Processor(const A &a)const
  {
   m_os << "got a A";
  }

  virtual void Processor(const B &b)const
  {
   m_os << "got a B";
  }

  virtual void Processor(const C &c)const
  {
   m_os << "got a C";
  }

  // etc
};

接着:

 OutputStream &operator<<(OutputStream &os, A &a)
 {
   PrintProcessor(os);
   a.Process(PrintProcessor);
   return os;
 }
于 2013-07-25T08:14:43.770 回答
0

您可以为打印责任提供一个接口,并将公共责任保留在您的类层次结构下。例子:

class Printer { public: virtual void print(OutputStream &os) = 0; }
class A { public: virtual ~A() {} ... };
class B: public A, public Printer { ... }; // this needs print function, use interface.
class C: public B { ... };
class D: public A { ... };
于 2013-07-25T08:34:13.960 回答