Question

I have a design issue that I constantly keep encountering.

For the sake of illustration, let's assume that I have a polymorphic class hierarchy

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

I want to be able to print instances of these classes in a polymorphic way, i.e. each class has its own way of printing itself. The obvious way of achieving this would be to add

virtual void print(OutputStream &os) = 0;

into the base class and override this method in every subclass. However, if the original responsibility of the classes is not related to printing, this will add another responsibility to them, thus violating SRP.

My question is: what is a proper way of achieving the desired behavior without violating SRP?

In this post, a solution based on the Visitor design pattern is proposed. However, then I would need to create a class which has to know about every subclass of A. I would like to be able to add and remove subclasses without a need to always modify the visitor.

Is there some other, SRP-preserving way than the two ways described above?

Was it helpful?

Solution

There is an acyclic visitor pattern that eliminates the need to know about every subclass. It relies on dynamic_cast, but may be what you need.

OTHER TIPS

There is nothing wrong with a class printing itself. It does not violate SRP because printing does not constitute a responsibility.

Remember that responsibility is defined as a reason to change. You don't change a class because your requirements for printing change. The class should only send name-value pairs to the entity responsible for printing, called the formatter. This procedure of sending name-value pairs never ever changes by itself. Any changes in it are only prompted by other changes, unrelated to printing (when you e.g. add a field, you also add its representation to the printing procedure).

The formatter should know nothing about the classes it prints, but simply present the name-value pairs according to some set of requirements. The formatter changes when the printing requirements change. Therefore printing would be the sole responsibility of the formatter.

You'll need to go for some sort of visitor of double dispatch solution in order to do this. The double dispatch approach is a bit more lightweight, so how about something like this:

In A:

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 {}
};

In A:

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

Then, in each derived class override Process with an identical definition:

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

This will ensure that the correct overload in Process is called.

Now, create a stream processor:

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

And then:

 OutputStream &operator<<(OutputStream &os, A &a)
 {
   PrintProcessor(os);
   a.Process(PrintProcessor);
   return os;
 }

You can provide an interface for printing responsibility and keep common responsibilities under your class hierarchy. Example:

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 { ... };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top