5
\$\begingroup\$

I asked this question on Stack Overflow and in the comments someone had this to say.

In a proper design, you should almost never have to do a dynamic_cast, even if it's hidden inside some nice getter function.

Now this got me thinking a lot. I have been thinking of various ways in which I could eliminate the dynamic_cast in question, or any cast for that matter from my code. Now I come here rather frustrated as the only thing I can think of to get rid of that cast; in that location is by doing the cast elsewhere.

So I ask, how can I improve upon this design?

#include <iostream>
#include <string>

// Properties for all components
class ComponentProperties {
public:
  ComponentProperties( std::string componentProperty ):
    componentProperty_( componentProperty ) { }

  virtual ~ComponentProperties(  ) {  }
  std::string getComponentProperty(  ) const { return componentProperty_; }

protected:
  std::string componentProperty_;
};

// Properties specific to Textboxes
class TextboxProperties: public ComponentProperties {
public:
  TextboxProperties( std::string componentProperty, std::string textboxProperty ):
    ComponentProperties( componentProperty ),
    textboxProperty_( textboxProperty ) {  }

  std::string getTextboxProperty(  ) { return textboxProperty_; }

private:
  std::string textboxProperty_;
};

class Component {
public:
  Component( ComponentProperties& properties ):
    properties_( properties ) {  }

  virtual ~Component(  ) {  }

protected:
  ComponentProperties& properties_;
};

class Textbox : public Component {
public:
  Textbox( TextboxProperties& properties ):
    Component( properties ) {  }

  // overload << purely for displaying properties.
  friend std::ostream & operator << ( std::ostream& out, const Textbox& textbox );
};

std::ostream & operator << ( std::ostream& out, const Textbox& textbox ) {  
  return out << textbox.properties_.getComponentProperty(  ) << ", "
             << dynamic_cast< TextboxProperties& >( textbox.properties_ ).getTextboxProperty(  );
}

int main(  ) {
  TextboxProperties properties( "PropertyOne", "PropertyTwo" );
  Textbox textbox( properties );

  std::cout << textbox << std::endl;
  return 0;
}
\$\endgroup\$
6
\$\begingroup\$

The best solution is to make ComponentProperties know how to print itself.

 class ComponentProperties {
     friend std::ostream& operator<<(std::ostream& s, ComponentProperties const& d) {
         return d.print(s);
     }
     virtual std::ostream& print(std::ostream& s) const {
         return s << componentProperty_;
     }
     //  STUFF
 };

 class TextboxProperties: public ComponentProperties {
     virtual std::ostream& print(std::ostream& s) const {
         return ComponentProperties::print(s) << ", " << textboxProperty_;
     }
     // STUFF
  };

  class Component {
      // PS. I tend to put my simple friend class definitions.
      //     inline within the class. They are after all part
      //     of the classes public interface and tightly bound
      //     to the implementation of the class.
      friend std::ostream& operator<<(std::ostream& out, const Component& component) {  
          return out << component.properties_;
      }
      // STUFF
  };

Advantage of doing it this way is you can get rid of this getter methods.

\$\endgroup\$
2
\$\begingroup\$

There are a number of ways you can remove the dynamic cast here. The first thing that comes to my mind in this case is the Decorator Pattern which will allow you to compose your properties classes.

Alternatively,

A simpler solution might be to make the getComponentProperty() method virtual, override it in TextboxProperties, and have the TextboxProperties version return a combination of both the componentProperty_ and textboxProperty_ formatted appropriately.

In this case it might be better to rename getComponentProperty() as something like getProperties() or getPropertiesAsString(). This would remove the need for the getTextboxProperty() method in your TextboxProperties class.

Ideally, you should think of your base class as the client interface to your derived classes. If user's only have access to pointers to a base class, they shouldn't need to know which derived classes exist at all, they should be able to accomplish their tasks with the base class public interface. The base class should then have points of customization (virtual functions) where derived classes can implement their own specific behaviors. At least, this is how it would work in an ideal situation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.