mercredi 26 décembre 2018

Correct way to deal with abstract classes and abstract members in C++

I am looking for a (possibly) better approach to solve a problem with abstract classes. This question is more about an alternative design than the implementation provided here (I willingly simplified it).

I have a basic Content interface class like this:

class Content {
public:
    virtual void info() = 0;
};

class ContentA : public Content {
public:
    void info() override { cout << "Content A" << endl; }
    int serial() { return 123; }
    int num() { return 456; }
};

class ContentB : public Content {
public:
    void info() override { cout << "Content B" << endl; }
    int identifier() { return 789; }
};

I have a Container class interface which has to contain a Content object:

class Container {
public:
    virtual shared_ptr<Content> content() = 0;
    virtual void info() = 0;
    void contentInfo() { content()->info(); }
};

class ContainerA : public Container {
public:
    ContainerA(shared_ptr<ContentA> content) : m_content(content) {}
    shared_ptr<Content> content() { return m_content; }
    void info() {
        auto identifier = m_content->serial() * m_content->num();
        cout << "Container A: " << identifier << endl;
    }
protected:
    shared_ptr<ContentA> m_content;
};

class ContainerB : public Container {
public:
    ContainerB(shared_ptr<ContentB> content) : m_content(content) {}
    shared_ptr<Content> content() { return m_content; }
    void info() {
        cout << "Container B: " << m_content->identifier() << endl;
    }
protected:
    shared_ptr<ContentB> m_content; 
};

Usage example would be:

auto contentB = make_shared<ContentB>();
ContainerB containerB(contentB);
containerB.info();
// => "Container B: 789"
containerB.contentInfo();
// => "Content B"

Although ContentA and ContentB both inherit from Container, they have their own specialized methods intended to be specifically used by ContainerA and ContainerB respectively. ContainerA can only interact with ContentA, and ContainerB can only interact with ContentB. You can't instantiate ContainerA by passing a ContentB to its constructor.

I want to enforce the fact that, in the future, anyone needing to create ContainerC will also need first to create ContentC. This is why the content() method is virtual pure.

I'm not sure using such content() method is the correct way to do this. Is it? I don't know. You can test it on Ideone here.

Basically, I thought it would reduce repetition if m_content was a member of the abstract class Container. But by doing so, it won't compile as the exact Content inherited class is not known from the Container. That would required casting the content object, which is worse. See what I mean here.

Aucun commentaire:

Enregistrer un commentaire