HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Avoiding casts in abstract types

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
abstractavoidingcaststypes

Problem

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 
#include 

// 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 ( textbox.properties_ ).getTextboxProperty(  );
}

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

  std::cout << textbox << std::endl;
  return 0;
}

Solution

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.

Code Snippets

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
  };

Context

StackExchange Code Review Q#42335, answer score: 6

Revisions (0)

No revisions yet.