How to avoid dropping when having an interface and base classes?

I'm sure I missed something elemental here, but I can't get around it.

Let's say we have several possible implementations of the Manager class, which processes objects of type Base . It should be possible to determine which implementation to use at runtime.

Based on the implementation of Manager , they will need to set and get specific properties from Base , which is why the DerivedA and DerivedB that they use internally. Is there a way around the need to lower the parameter in Handle methods in order to move on to implementation-specific properties?

 class Base { /* Abstract class with common properties */ }; class DerivedA : public Base { /* DerivedA-specific properties */ }; class DerivedB : public Base { /* DerivedB-specific properties */ }; class IManager { /* These functions must be implemented by every Manager implementation */ public: virtual Base* Create() = 0; virtual void Handle(Base*) = 0; }; class AManager : public IManager { public: Base* Create() override { return new DerivedA(); } void Handle(Base* pFoo) override { // Now if we want to access pFoo specific properties, we will need to dynamic_cast it } }; class BManager : public IManager { public: Base* Create() override { return new DerivedB(); } void Handle(Base* pBar) override { /* same here */ } }; void Run(bool useAManager) { IManager* pManager = nullptr; if (useAManager) pManager = new AManager(); else pManager = new BManager(); Base* pData = pManager->Create(); /* use Base specific properties ... */ pManager->Handle(pData); } 

Edit: Thank you all for your valuable contribution. I will accept the message @ jpo38, as it provides a possible solution to this problem. However, after some consideration, I found that the main problem in class design.

+7
c ++ design-patterns
source share
2 answers

You can use the template. In your example, this would be:

 class DerivedA; class DerivedB; class Visitor { public: virtual void visitA( DerivedA& a ) = 0; virtual void visitB( DerivedB& b ) = 0; }; class Base { public: virtual void Accept( Visitor& visitor ) = 0; }; class DerivedA : public Base { public: virtual void Accept( Visitor& visitor ) { visitor.visitA( *this ); } }; class DerivedB : public Base { public: virtual void Accept( Visitor& visitor ) { visitor.visitB( *this ); } }; 

Then from AManager or BManager:

 void Handle(Base* pFoo) { class MyVisitor : public Visitor { public: virtual void visitA( DerivedA& a ) { // do somethiong specific to a, you have access to DerivedA } virtual void visitB( DerivedB& b ) { // do somethiong specific to b, you have access to DerivedB } }; MyVisitor v; pFoo->Accept( v ); } 

The drawback of the visitor template is that you will need to define a new class of visitors each time you want to do something specific.

You can also think about this (but I definitely recommend to visitors, really welcome if you add DerivedC later or want to share some specific operation through the common classes of visitors).

 class Base { public: virtual DerivedA* GetAsA() = 0; virtual DerivedB* GetAsB() = 0; }; class DerivedA : public Base { public: virtual DerivedA* GetAsA() { return this; } virtual DerivedB* GetAsB() { return NULL; } }; class DerivedB : public Base { public: virtual DerivedA* GetAsA() { return NULL; } virtual DerivedB* GetAsB() { return this; } }; 

Then from AManager or BManager:

 void Handle(Base* pFoo) { if ( pFoo->GetAsA() ) { // use GetAsA to access DerivedA object avoiding downcasting } if ( pFoo->GetAsB() ) { // use GetAsB to access DerivedB object avoiding downcasting } } 
+2
source share

Not really. If you absolutely need to treat certain subtypes differently, dynamic_cast is the cleanest solution.

Strictly speaking, the real problem here begins with the word "properties." An object-oriented base class has no properties, but no operations, and when you accept the Base parameter, all that interests you is those abstract operations. In completely clean object-oriented designs, at least.

The design of your class is simply not purely object-oriented, but that’s it. But this is not a problem in itself. If this works for you and the code is easy to read and maintain, then everything is fine.

0
source share

All Articles