Dependency Inversion Principle Dilemma

T

Thomas Matthews

Hi,

According to Robert Martin's Dependency Inversion Principle,
http://www.objectmentor.com/resources/articles/dip.pdf,
when there is a need to test the type of an object, the
code inside the "switch cases" should be placed into the
parent class.

However, I am finding that this conflicts with the other
principles -- the objects now must know details about
objects outside their own encapsulation.

For example, using the Shape class:
class Shape
{
virtual void draw() = 0;
};

class Square
: public Shape
{
void draw();
};

class Triangle
: public Shape
{
void draw();
};

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).

So how does one implement (or not violate) the Dependency
Inversion principle without violating the encapsulation
by require details of external interfaces?
Can one keep a Triangle simplified so it is just a
Triangle class?

Otherwise, one would have to keep modifying the base
Shape class everytime a new display device comes up.
{Or any other entity that would need to know the
type of shape}.


--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
 
R

Rolf Magnus

This will only be a "me too" answer, I'm afraid.

Thomas said:
Hi,

According to Robert Martin's Dependency Inversion Principle,
http://www.objectmentor.com/resources/articles/dip.pdf,
when there is a need to test the type of an object, the
code inside the "switch cases" should be placed into the
parent class.

However, I am finding that this conflicts with the other
principles -- the objects now must know details about
objects outside their own encapsulation.

For example, using the Shape class:
class Shape
{
virtual void draw() = 0;
};

class Square
: public Shape
{
void draw();
};

class Triangle
: public Shape
{
void draw();
};

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).

That's exactly the problem that I have with this. A triangle should not
know that it can be drawn, or what class is responsible for drawing
triangles. It should only be a container for the three vertices and
maybe contain some triangle specific functions (area, center of mass or
whatever).
So how does one implement (or not violate) the Dependency
Inversion principle without violating the encapsulation
by require details of external interfaces?
Can one keep a Triangle simplified so it is just a
Triangle class?

Sorry, I don't know any good way, but I'd be interested in it, too.
Otherwise, one would have to keep modifying the base
Shape class everytime a new display device comes up.
{Or any other entity that would need to know the
type of shape}.

Not only the base class, but also every derived one. And if you want to
reuse your class hierarchy e.g. for writing a file conversion tool
can't draw at all, you'd either have to make a dummy renderer or edit
all the classes to remove the draw function.
 
A

Alf P. Steinbach

class Shape
{
virtual void draw() = 0;
};

class Square
: public Shape
{
void draw();
};

class Triangle
: public Shape
{
void draw();
};

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).

Apart from the above being nonsense C++ (it would be better with
'struct' instead of 'class') this is off-topic in this group.

Try instead e.g. [comp.programming].





So how does one implement (or not violate) the Dependency
Inversion principle without violating the encapsulation
by require details of external interfaces?

Use an abstract interface.


struct ICanvas
{
virtual void setPixel( Point const& pos, Color const& aColor = Color::black ) = 0;
};


struct Shape
{
virtual void drawAt( Point const& upperLeft, ICanvas& aCanvas ) = 0;
};
 
J

Jeffrey Schwab

Thomas said:
Hi,

According to Robert Martin's Dependency Inversion Principle,
http://www.objectmentor.com/resources/articles/dip.pdf,
when there is a need to test the type of an object, the
code inside the "switch cases" should be placed into the
parent class.

That's the traditional OO approach. Sometimes it's appropriate, but
often it's not.
However, I am finding that this conflicts with the other
principles -- the objects now must know details about
objects outside their own encapsulation.

For example, using the Shape class:
class Shape
{
virtual void draw() = 0;
};

class Square
: public Shape
{
void draw();
};

class Triangle
: public Shape
{
void draw();
};

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).

So how does one implement (or not violate) the Dependency
Inversion principle without violating the encapsulation
by require details of external interfaces?
Can one keep a Triangle simplified so it is just a
Triangle class?

Otherwise, one would have to keep modifying the base
Shape class everytime a new display device comes up.
{Or any other entity that would need to know the
type of shape}.

// Use overloaded functions.

namespace Shapes
{
class Shape { /* ... */ };
class Square: public Shape { /* ... */ };
class Triangle: public Shape { /* ... */ };
}

namespace Display
{
void draw( Shapes::Shape const& ) { /* ... * }
void draw( Shapes::Square const& ) { /* ... * }
void draw( Shapes::Triangle const& ) { /* ... * }
}
 
T

Thomas Matthews

Alf said:
class Shape
{
virtual void draw() = 0;
};

class Square
: public Shape
{
void draw();
};

class Triangle
: public Shape
{
void draw();
};

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).


Apart from the above being nonsense C++ (it would be better with
'struct' instead of 'class') this is off-topic in this group.

Try instead e.g. [comp.programming].
I believe that technically you are correct that is is not so
much a language issue as an OO one.

The classes are nonsense but are the typical example used
by many authors and instructors. I thought this would
be more familiar to readers than my current implementation.
Use an abstract interface.


struct ICanvas
{
virtual void setPixel( Point const& pos, Color const& aColor = Color::black ) = 0;
};


struct Shape
{
virtual void drawAt( Point const& upperLeft, ICanvas& aCanvas ) = 0;
};
In your example, the Shape class still has a drawAt method.
If I want to store shapes into a database, then I would have
to change the Shape class and add a method for database
access:
enum Action_Type(DB_STORE, DB_RETRIEVE, DB_SEARCH};

struct Shape /* or class */
{
virtual void draw(); // or drawAt
virtual void database(Action_Type);
};

In my application, a Reference Database System, references can
be created by Text_Stream, User_Interface and Database. In a
simple situation, the Text_Stream creates a reference subobject
(like book, magazine, newspaper, etc.) and returns a pointer
to the base class, "Reference". The next step is to display
the object via the User_Interface. This is where the dilemma
begins. I would like to have each reference stored in a
different "view" (i.e. page, listbox, panel, etc.) based on
its kind (type). For example, all of the books would be
displayed together as will all the magazines.

The D.I.P. states that the objects should have a "display"
method. However, in order for the object to display its
data, it must know some details about the User_Interface.
Thus the violation of the Integration Separation Principle (ISP).

Although one idea is to create an interface class for
references with interactions with the User_Interface:
struct Reference
{
};

struct Reference_with_UI
: public Reference
{
virtual void display(/*???*/);
};

struct Book
// : public Reference /* original inheritance */
: public Reference_with_UI
{
virtual void display(/* ??? */);
}

The above implementation takes the interface out of the
base class, but the child classes still have to know
details about the User_Interface (such as which view
to display in).

Perhaps, as you suggested, I should move this over to
or
--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
 
T

Thomas Matthews

Jeffrey said:
That's the traditional OO approach. Sometimes it's appropriate, but
often it's not.
Agreed. Sometimes pure OO is dificult.


// Use overloaded functions.

namespace Shapes
{
class Shape { /* ... */ };
class Square: public Shape { /* ... */ };
class Triangle: public Shape { /* ... */ };
}

namespace Display
{
void draw( Shapes::Shape const& ) { /* ... * }
void draw( Shapes::Square const& ) { /* ... * }
void draw( Shapes::Triangle const& ) { /* ... * }
}
Now the dilemma moves to the draw() function. The draw()
function needs to get the data from the Shape. Although
I do like this version better.

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
 
P

Programmer Dude

Thomas said:
The D.I.P. states that the objects should have a "display"
method. However, in order for the object to display its
data, it must know some details about the User_Interface.
Thus the violation of the Integration Separation Principle (ISP).

Not necessarily. By definition a "shape" is a displayable thing,
so it can certainly possess *some* knowledge about that. Ideally,
that knowledge should be very abstract and designed to work with
varying types of displays.

I think a good technique--which you've touched on already--is that
your draw methods take a parameter that is an interface to the
"canvas". By deriving from the "canvas" class, you can allow your
shapes to drawn on anything.
 
J

Jeffrey Schwab

Thomas said:
Agreed. Sometimes pure OO is dificult.



Now the dilemma moves to the draw() function. The draw()
function needs to get the data from the Shape. Although
I do like this version better.

The information needed by the display can be provided by accessors in
the Shape classes. If the information needed to display the objects
really should not be public, but is an implementation detail, then so is
the act of displaying. In that case, draw *should* be a member of the
original Shape classes.

-Jeff
 
E

EventHelix.com

The above "draw" methods must know something about the
external environment in order to draw their shape. I
believe this conflicts with the Integration Separation
Principle as well as the K.I.S.S. (Keep It Simple (and)
Stupid).

The solution is that the draw method should draw to an
abstract device context. Thus you will not need to modify
any of the shape family of classes when a new display device
is introduced.

BTW, Here is another article on the Dependency Inversion Principle:


http://www.eventhelix.com/RealtimeMantra/Object_Oriented/dependency_inversion_principle.htm

Sandeep
 
K

Kevin Cline

Rolf Magnus said:
This will only be a "me too" answer, I'm afraid.



That's exactly the problem that I have with this. A triangle should not
know that it can be drawn, or what class is responsible for drawing
triangles. It should only be a container for the three vertices and
maybe contain some triangle specific functions (area, center of mass or
whatever).

No, a triangle should be whatever it is needs to be to meet the
application requirements, consistent with the name "Triangle". I
wouldn't like to find a Triangle class that really implemented a
quadrilateral, but other than that there's no way to say what should
or should not be supported by a Triangle class. It's an engineering
decision. Throwing functions out of the Triangle class means that
they have to be somewhere else. If you throw out draw(), then instead
of just Triangle, you might have Triangle and PolygonDrawer. Is that
a simpler, or more complex? There's no way to answer that without
seeing the rest of the application.
 
N

Nicholas Hounsome

That's exactly the problem that I have with this. A triangle should not
On the contrary - A triangle should know that it can be drawn if the
application requires it to be drawn.
To see that this is so just consider the more common

virtual void print(ostream&) const = 0;

I doubt that you would argue that it shouldn't know how to print itself and
drawing is just printing in 2D.

The reason that most people don't see it this way is that whilst C++ has a
standard way of printing characters it has no standard
way of drawing. I was brought around to this point of view by an excellent
article on Java which does have a standard GUI (Unfortunately Java people
still mainly write non-object oriented code because of beans but this is
just warping the OO paradigm to suit tool vendors)

It is quite likely that you actually need more than one type of triangle -
one to represent the basic mathematical abstraction and one to do the more
complicated stuff either deriving from the simple class or containing it.
This is also a very common situation and totally in keeping with KISS.
 
M

Mike Smith

Nicholas said:
On the contrary - A triangle should know that it can be drawn if the
application requires it to be drawn.
To see that this is so just consider the more common

virtual void print(ostream&) const = 0;

Just 'cause it's common doesn't necessarily mean it's the best way.
This kind of design leads to huge, bloated classes. OK, so you think
your triangle needs to know how to print itself, and how to draw itself.
What's next? Computations of things like area, centroid, etc. OK,
after that? How about rotation/translation/other transforms? In 2D?
How about in 3D? After all, a 2D triangle can exist within a 3D space.
Then maybe we'll add some real-time animation, serialization,
collision detection, etc., etc., etc. See what I mean? What started
out as a nice fifty-line class could end up being a 100K source file,
and so fat and bloated that it's of no use to anyone without a 40-hour
seminar and an O'Reilly animal-cover book.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,830
Latest member
ZADIva7383

Latest Threads

Top