By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,194 Members | 889 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,194 IT Pros & Developers. It's quick & easy.

Dependency Inversion Principle Dilemma

P: n/a
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

Jul 22 '05 #1
Share this Question
Share on Google+
12 Replies


P: n/a
This will only be a "me too" answer, I'm afraid.

Thomas Matthews wrote:
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.

Jul 22 '05 #2

P: n/a
On Thu, 18 Dec 2003 18:32:54 GMT, Thomas Matthews <Th****************************@sbcglobal.net> wrote:
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;
};

Jul 22 '05 #3

P: n/a
Thomas Matthews wrote:
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& ) { /* ... * }
}

Jul 22 '05 #4

P: n/a
Alf P. Steinbach wrote:
On Thu, 18 Dec 2003 18:32:54 GMT, Thomas Matthews <Th****************************@sbcglobal.net> wrote:

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

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
news:comp.programming or news:comp.object.

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

Jul 22 '05 #5

P: n/a
Jeffrey Schwab wrote:
Thomas Matthews wrote:
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.

Agreed. Sometimes pure OO is dificult.
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& ) { /* ... * }
}

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

Jul 22 '05 #6

P: n/a
Thomas Matthews wrote:
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.

--
|_ CJSonnack <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Jul 22 '05 #7

P: n/a
Thomas Matthews wrote:
Jeffrey Schwab wrote:
Thomas Matthews wrote:
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.


Agreed. Sometimes pure OO is dificult.
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& ) { /* ... * }
}

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

Jul 22 '05 #8

P: n/a
> 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/RealtimeMa..._principle.htm

Sandeep
--
http://www.EventHelix.com/EventStudio
EventStudio 2.0 - Generate Sequence Diagrams and Use Case Diagrams in
PDF
Jul 22 '05 #9

P: n/a
EventHelix.com wrote:
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


/A/ 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/RealtimeMa..._principle.htm

Sandeep
--
http://www.EventHelix.com/EventStudio
EventStudio 2.0 - Generate Sequence Diagrams and Use Case Diagrams in
PDF


Jul 22 '05 #10

P: n/a
Rolf Magnus <ra******@t-online.de> wrote in message news:<br*************@news.t-online.com>...
This will only be a "me too" answer, I'm afraid.

Thomas Matthews wrote:
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).


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.
Jul 22 '05 #11

P: n/a
> > 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).


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.


Jul 22 '05 #12

P: n/a
Nicholas Hounsome wrote:
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).

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.

--
Mike Smith

Jul 22 '05 #13

This discussion thread is closed

Replies have been disabled for this discussion.