I am currently refactoring some legacy code and would like to factorize a multiple if...elseif... statement into a series of classes implementing various strategies.
Since I have to access the original object's internals, I'm going to declare the new classes as nested classes; since nobody from the external world should access them, I'd prefer to declare them in private scope.
For the sake of exposing as few implementation details as possible, I was wondering whether it's possible to only forward-declare the base strategy class in the header file, and place all subclasses declaration in the implementation file. Code example as follows:
-- header file
class MyUglyClass
{
private:
class IStrategyBase;
IStrategyBase* sPtr;
// class ActualImplementation; // this is what I'd like to avoid
// class YetAnotherImplementation; // as above
// blah blah blah
};
-- implementation file
class MyUglyClass::IStrategyBase
{
virtual ResultType DoSomething(SomeType someParameter) = 0;
// could expose some MyUglyClass members, since
// derived classes wouldn't inherit friendship
};
class ActualImplementation: public MyUglyClass::IStrategyBase
{
ResultType DoSomething(SomeType someParameter) override
{
// Do actual work
}
}
class YetAnotherImplementation: public MyUglyClass::IStrategyBase
{
ResultType DoSomething(SomeType someParameter) override
{
// Doing something really tricky & clever for corner cases
}
}
Of course the compiler complains since IStrategyBase is not accessible; I could work around this by fwd-declaring ActualImplementation and YetAnotherImplementation into the header file together with IStrategyBase, but I'd rather avoid this, since I would need to change the header if a new strategy was needed.
I could also declare IStrategyBase in public scope, however I would prefer to keep it private to avoid other people messing with it.
Of course I'm assuming that non-fwd-declared subclasses wouldn't inherit friendship with MyUglyClass, so I would have to expose relevant data the IStrategyBase protected members.
Is there any way to achieve this I could be missing?
EDIT:
Thanks to all folks who commented, I realized that nobody could mess with IStrategyBase class even if declared in public scope, since class definition would be buried in the implementation file as well. What I'm wondering now is if I could make derived classes access internals of MyUglyClass without having to fwd declare them together with IStrategyBase. I guess answer is "no", since friendship is not inherited, but perhaps there is some more C++ perk I'm missing.
One possibility (this is not the pimpl idiom, just an accessibility hack):
class MyUglyClass
{
private:
struct Impl; // Is automatically "friend struct Impl;"
class IStrategyBase;
IStrategyBase* sPtr;
// class ActualImplementation; // this is what I'd like to avoid
// class YetAnotherImplementation; // as above
// blah blah blah
};
class MyUglyClass::IStrategyBase
{
public:
virtual int DoSomething(int someParameter) = 0;
// could expose some MyUglyClass members, since
// derived classes wouldn't inherit friendship
};
struct MyUglyClass::Impl
{
class ActualImplementation: public MyUglyClass::IStrategyBase
{
int DoSomething(int someParameter) override
{ (void) someParameter; return 1;}
};
class YetAnotherImplementation: public MyUglyClass::IStrategyBase
{
int DoSomething(int someParameter) override
{ (void) someParameter; return 2; }
};
};
int main() {}
If you want to conceal any imlementation details you can use pImpl idiom (pointer to implementation) aka Opaque pointer https://en.wikipedia.org/wiki/Opaque_pointer So you can change your code like this
-- header file
#include <memory>
class MyUglyClass
{
MyUglyClass();
~MyUglyClass(); // destructor must be only declared to avoid problems
// with deleting just forwarded inner class
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
-- implementation file
class MyUglyClass::Impl
{
class IStrategyBase;
IStrategyBase* sPtr;
class ActualImplementation; // now these classes safely hidden inside .cpp
class YetAnotherImplementation; // Nobody can reach them.
};
class MyUglyClass::Impl::IStrategyBase
{
virtual ResultType DoSomething(SomeType someParameter) = 0;
// could expose some MyUglyClass members, since
// derived classes wouldn't inherit friendship
};
class ActualImplementation: public MyUglyClass::Impl::IStrategyBase
{
ResultType DoSomething(SomeType someParameter) override
{
// Do actual work
}
};
class YetAnotherImplementation: public MyUglyClass::Impl::IStrategyBase
{
ResultType DoSomething(SomeType someParameter) override
{
// Doing something really tricky & clever for corner cases
}
};
MyUglyClass::MyUglyClass() : pImpl(new Impl()) {}
MyUglyClass::~MyUglyClass() {} // let the unique_ptr do its work
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With