Michael
Michael

Reputation: 1502

design of class inheritance/compostion

I do have two classes: a reader and a writer. For both classes I have an abstract interface since the data source/target should be flexible:

class abstract_reader {
...
};

class concrete_reader : public abstract_reader {
   DATATYPE m_data;
...
};    
class abstract_writer {
...
};

class concrete_writer : public abstract_writer {
   DATATYPE m_data;
...
};

The writer shall have both functionalities, to read and to write The implementation of the reading part of the concrete_writer is the same as the implementation of the concrete_reader. What would be a good way to combine these classes?

Upvotes: 2

Views: 134

Answers (4)

James Kanze
James Kanze

Reputation: 153899

First, if it's just a writer, it shouldn't know how to read. What happens when you implement a writer to the screen? Still, having a ReaderWriter, in addition to Reader and Writer, is a very reasonable choice, and that creates the same problem.

The way I would do this is first, to define the ReaderWriter as an interface which inherits from both Reader and Writer:

class Reader
{
private:
    //  Pure virtual functions to the implementation...
public:
    virtual ~Reader() {}
    //  Interface...
};

class Writer
{
private:
    //  Pure virtual functions to the implementation...
public:
    virtual ~Writer() {}
    //  Interface...
};

class ReaderWriter : public virtual Reader, public virtual Writer
{
    //  Just joins the two; adds nothing new of its own
};

Note the virtual inheritance. This should generally be the default when you're extending interfaces. Once you've got that, you can (usually) use mixins for the implementation:

class ConcreteReader : public virtual Reader
{
    //  Overrides for the pure virtual functions, + any data needed.
public:
    ConcreteReader();   //  Or with parameters, as needed.
};

class ConcreteWriter : public virtual Writer
{
    //  Overrides for the pure virtual functions, + any data needed.
public:
    ConcreteWriter();   //  Or with parameters, as needed.
};

class ConcreteReaderWriter : public ReaderWriter, public ConcreteReader, public ConcreteReaderWriter
{
};

Client code which receives a Reader* can read; client code which receives a Writer* can write; and client code which receives a ReaderWriter can do either. And of course, if the client has a Reader*, it can always try to dynamic_cast it to a Writer* or a ReaderWriter*, and if the dynamic_cast succeeds, it can write as well.

Edit: I forgot to mention: this technique is known as mixins.

Upvotes: 1

Pete Becker
Pete Becker

Reputation: 76235

class concrete_writer : public abstract_writer, public abstract_reader {
public:
    void read() { // or whatever the proper override is for abstract_reader
        reader.read();
    }
private:
    concrete_reader reader;
};

But, as @Bartek pointed out, it seems odd to have a class named "writer" that also reads.

Upvotes: 1

SingerOfTheFall
SingerOfTheFall

Reputation: 29966

What you want is a little confusing, but there is a number of ways:

  • Derive the writer from both abstract classes
  • Hold a pointer to the concrete reader in the writer
  • Hold an instance of the concrete reader in the writer
  • Remove the reading functionality from the writer and make a third class that can both read and write.

Personally, I would follow the last way, then you will have a full set of classes for reading-only, writing-only, and for doing both.

Upvotes: 2

Bartek Banachewicz
Bartek Banachewicz

Reputation: 39370

Why would the writer want to know how to read? Maybe you should think about the 3rd class, incorporating BOTH reading and writing functionalities.

If you're convinced that it's OK, just derive the writer from both abstract_reader and abstract_writer. As long as they are properly-implemented interfaces (no fields, for example), everything should work just fine.

And oh, maybe it would be nice to incorporate templates (I've noticed the mysterious DATATYPE)

Upvotes: 1

Related Questions