7

So maybe this is a dumb question and I'm over thinking this, but I have the following situation. I am making a "class Shell" which can run abstract "class Action" objects. It is the only class that should create or use these objects. Action objects need access to the Shell to perform specific actions on it, but I'm trying to avoid adding public interfaces for this (no one else should be allowed to do that).

I originally had a simple (not so elegant)

class Shell
{
 public:
    bool checkThing();
    // etc...
 private:
    bool _thing;
};

class Action
{
 public:
    virtual void execute( Shell &s )=0;
};

class ChangeAction : public Action
{
 public:
    void execute( Shell &s )
    {
        // requires friendship or public mutator!
        s._thing = true;
    }
};

So I considered a nested class Action, but I wanted to make it private (why let anyone else make concrete Actions except the Shell, right?)

class Shell
{
 public:
    bool checkThing();
    // etc...
 private:
    bool _thing;
    class Action;
};

class Shell::Action
{
 public:
    virtual void execute( Shell &s )=0;
};

class ChangeAction : public Shell::Action
{
 public:
    void execute( Shell &s )
    {
        // ok now!
        s._thing = true;
    }
};

But of course I can't inherit from Action any more (that makes sense, it's private). So that doesn't work.

So my question, should I just go with the first method and friendship or a public interface? Can I use something similar to the second method to keep that relationship with Actions and the Shell? Do you have a better idea?

Matt
  • 5,478
  • 9
  • 56
  • 95

2 Answers2

4

If the only code that needs to be able to see Action is Shell, one option would be to forward-declare Action in the header file but only define the class in the .cpp file. This would then let you declare as many Action subclasses as you'd like in the implementation file without letting anyone else subclass off of Action because no one else would have a complete class definition for Action. This also avoids any need for public interfaces or friend declarations - all the Action classes are declared in the global scope, but shielded from other files by virtue of being declared in the .cpp file.

Great question, by the way!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • What if Shell needs to create concrete Action classes? With this method, Shell doesn't know about the concrete classes, so it seems like I need a factory. Where does that belong though? At first I was thinking Shell would handle this itself, but do I need a separate Factory class? Thanks for the suggestions! – Matt Feb 16 '11 at 16:12
  • @Matt- if all the Actions are declared in the .cpp file along with the code for Shell, then Shell should be able to instantiate them directly. You could also make a factory if you want, but if you do that should also go in the .cpp file. The idea is to keep all implementation details in the .cpp file where other parts of the code can't see them. – templatetypedef Feb 16 '11 at 18:36
  • Sorry, I thought I understood this, but I'm a little confused. Should `Action` still be a nested class of `Shell`? You say I don't need to make `Action` a friend of `Shell`, but how do I let the subclasses of `Action` access private members of `Shell`? – Matt Feb 17 '11 at 06:41
  • @Matt- Ah, if Action needs to see the private internals of Shell, you're going to have to make it a friend if you don't want to expose the internals of the class to everyone. I didn't notice that requirement. – templatetypedef Feb 17 '11 at 10:00
  • Ok thanks, this does seem like a good approach. I have been rethinking just adding a public interface for things Actions need because they aren't completely violating encapsulation by making them public, and I kind of like it better than giving Action access to private data of Shell. Still, thanks for your help! – Matt Feb 17 '11 at 18:22
0

You can can use a combination of the methods: Basically just take all your classes from the first method and move them into the private section of the Shell class:

class Shell {
public:
    bool checkThing();     // etc...
private:
    bool _thing;

    class Action {
    public:
        virtual void execute( Shell &s )=0;
    };

    class ChangeAction : public Action
    {
    public:
        void execute( Shell &s )
        {
            // ok now!         s._thing = true;
        }
    }; 

};
Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72