Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Related function in different classes

0 views
Skip to first unread message

Vitaly Belman

unread,
Jul 17, 2001, 10:02:31 PM7/17/01
to
In some designs it happens when in one class a function has to call a
function in another class. While this function in the second class
must call the function in the first class if it was called first. See
the following example:

[ccode]
class CBrush
{
public:

void Draw(); // Definition of this function will be discussed.
// Paints the picture.

private:

bool m_IsInUse;
// Tells if the brush is currently in use (Drawing).

CPainter* m_Painter;
// The painter that uses this brush. NULL if none.

};

class CPainter
{
public:

void Draw()
// Causes the painter to draw.
{
if(m_Brush)
{
m_IsDrawing = true;
m_Brush->Draw();
}
}

bool IsDrawing();
// Tells if the Painter is currently drawing.

private:

bool m_IsDrawing;
// Tells if the the paiter is currently painting.

CBrush* m_Brush;
// The brush this painter uses. NULL if none.
};
[/ccode]

When a painter holds a brush and starts to CPainter::Draw() then the
Brush::IsInUse is obviously needs to change to 'true' since it is in
use. However, since Brush is a different class Painter has to call
some public function to effect the brush, the function is
CBrush::Draw(). However, what if someone else activates the
CBrush::Draw()? The function is public and if it runs the result can
be disastrous - The brush (if it has a painter) will be "painting" but
its owner, the painter will not know about it, CPainter::IsDrawing()
in this case will be still 'false'. Here are the possible solutions I
can see for that problem:

1) Making double check:
In this case the CBrush::Draw will be difined in the following way:

[ccode]
void CBrush::Draw()
{
if(m_Painter && m_Painter->IsDrawing())
m_IsInUse = true;
else
{
m_Painter->Draw();
return;
}
}
[/ccode]

Pros: We solve the situation here by causing the CBrush::Draw() to
check if it was called before CPainter::Draw() and if did then it
would call the CPainter::Draw() first.
Cons: The way works well in most of the situations but sometimes it
can be annoying as it can be somewhat hard to tell from the second
function what exactly changed and to check it and can cause unwanted
changes in one of the classes.

2) Private and friends:
That way I make CBrush::Draw private in the Brush class and let the
CPainter class be a friend of CBrush, that way the CBrush::Draw()
picture will look like this:

[ccode]
void CBrush::Draw() //private
{
if(m_Painter)
m_IsInUse = true;

}
[/ccode]

Pros: That way everything is safe because only CPainter can access
CBrush::Draw() and there is no risk that someone else will do it.
Cons: That way I give the CPainter ALL the access to CPaint while it
is fairly safe it means that the CPainter will need to know all the
CBrush content and be careful not to mess with some other private data
CBrush.

3) Dismissing the problem:
That way I'll come from a more logical point of view, why to defend
CBrush::Draw if no one can access in the first place? If the Painter
won't return a reference of Brush or only returns const reference then
no one can use CBrush::Draw().

[ccode]
void CBrush::Draw() //public
{
if(m_Painter)
m_IsInUse = true;

}
[/ccode]

Pros: Safe - No one can access CBrush but the one who has its pointer
- CPainter.
Cons: But other classes can still create CBrush, assign it to CPainter
and ruin the whole plans in some other class. Also, is it right at all
to make an imperfect function public that running it may cause
troubles?

What do you think?

Victor Bazarov

unread,
Jul 17, 2001, 11:28:50 PM7/17/01
to
"Vitaly Belman" <vit...@bigfoot.com> wrote...

> In some designs it happens when in one class a function has to call a
> function in another class. While this function in the second class
> must call the function in the first class if it was called first. See
> the following example:
>
> [ccode]
> [...]
>
> What do you think?

I think that it's one of two: either it's bad design and has to be
re-approached, or it's a bad example and you have to find a better
one to make your point.

Painter paints. Brush doesn't paint. It gives off colour. From
the world point of view someone of the two has to come first. When
I walk down the street and see somebody working on a sketch or
a painting, I see the person, not the instrument. See what I mean?

Painter::paint()
{
picture.coordinates() = currentbrush.location();
picture.receivestroke(currentbrush.colour(), currentbrush.width());

// repeat if needed
}
IMHO, there is never a need to call Brush::paint, or there is no
such method at all.

Victor
--
Please remove capital A's from my address when replying by mail


Mike P.

unread,
Jul 18, 2001, 5:23:23 AM7/18/01
to
Let's think this at design level, not at class level.
What actually is needed here is a way to centralize control over drawing as
both brush and paint can trigger drawing. So as this idea says itself, you
need to encapsulate control over drawing, and centralize it in a separate
object which brush and paint can access.
The design pattern is called Mediator and encapsulate communication between
objects.
Think about it: have a mediator object which both Brush and Painter use, and
when they need to paint, they simply call mediator->Draw ()
State about drawing is kept in the mediator, so it's easier. If painting is
already in progress, calls to other Draw are simply ignored.
Mediator->Draw calls the Draw mehods of all of its subjects (Brush and
Painter).
Getting down to implementing this: you can make draw protected or private in
Brush and Painter, so draw it cannnot be called directly by the clients.
Instead you have a public Draw which simply calls mediator->Draw ().
Mediator Draw calls these private or protected draws which actually do the
job. So mediator should be friend of Brush and Painter.
Anyway, you can further investigate other design patterns, you might find
other usefull ones.
HTH

"Vitaly Belman" <vit...@bigfoot.com> wrote in message
news:47367956.01071...@posting.google.com...

Vitaly Belman

unread,
Jul 18, 2001, 9:11:12 AM7/18/01
to
Using meditator is an intresting way.. However, how different is it
from my solution #2? Making both, Brush and Painter friends of the
Meditator class will disclose all their information to that Meditator
class.. How good is it? How better is it from just disclosing brush to
painter and making the CBrush::Draw() private?

---
Vitaly Belman
ICQ: 1912453

"Mike P." <nom...@nomail.com> wrote in message news:<Rxc57.469$2Y.3247@NewsReader>...

Mike P.

unread,
Jul 18, 2001, 11:07:14 AM7/18/01
to
It's better because it decouples brush from painter, so they don't have to
know each other, and the solution is more reusable. Moreover you can add
more than just the brush and the painter to the mediator in the future. It's
the difference between thinking locally and thinking globally, or in terms
of classes rather than design. For your local problem they might equally
provide a solution, but i think the solution with the mediator is much more
reusable, as brush and painter are not tightly coupled, so they can evolve
separately, modifying one doesn't mean you need to change the other.
Mediator assures that subclasses of brush and painter act as their parents,
and syncronize drawing the same way, as they must draw thru the mediator.
Otherwise it would be easy to forget to deliberately modify the behaviour of
some subclasses. Moreover you can add dynamically brush and painters at
run-time to the mediator (or perhaps pairs of brush and painter). Mediator
can be a singleton, so you can use just an instance of mediator for
different brush-painter association, so you don't need a mediator for each
brush-painter.
Finally, mediator is a classic well known pattern, so your users, or other
programmers in your team will understand easier your code, and you can refer
to the technique by name: eg: I used a Mediator, rather than explaining the
whole mechanism.
Anyway, it was just a suggestion, both solutions will work for you.

Vitaly Belman

unread,
Jul 18, 2001, 6:31:09 PM7/18/01
to
Thanks for your answers, I found the Patterns book you were talking
about and I'll read it now.

---
Vitaly Belman
ICQ: 1912453


"Mike P." <nom...@nomail.com> wrote in message news:<dAh57.779$2Y.7971@NewsReader>...

0 new messages