[C++] Question regarding Composite pattern

Started by
6 comments, last by SeymourClearly 16 years, 2 months ago
Hi, After having a few problems designing a command-based system for my RPG, I decided to do some research into design patterns in general and managed to borrow a copy of Design Patterns - Elements of Reusable Object Oriented Software, in order to strengthen my knowledge in this area. Instead of just finding the pattern I needed for my command system I've decided go through the whole thing. The book is going into detail about how to use the Composite pattern and it says the following:
Quote: The subclasses Line, Rectangle, and Text, define primitive graphical objects. These classes implement Draw to draw lines, rectangles, and text, respectively. Since primitive graphics have no child graphics, none of these subclasses implements child-related operations.
It's describing Graphic as an abstract base class, with the following methods:

Draw()
Add(Graphic)
Remove(Graphic)
GetChild(int)
The classes, Line, Rectangle and Text, all derive from Graphic but only implement the Draw method. This is where my question comes in. How do you properly go about hiding specific methods from the child, as it states above? Here's what I've come up with as a little test, which seems to work, but I was wondering if this is the correct approach:

class Parent
{
public:
	virtual void Draw() = 0;
	virtual void Add(/* blah */) = 0;
	virtual void Remove(/* blah */) = 0;
};

class Child : public Parent
{
private:
	void Add(/* blah */) {}
	void Remove(/* blah */) {}

public:
	void Draw() { cout << "Drawing child." << endl; }
};


Then, using the following:

int main()
{
	Child child1;
	child1.Draw();
	child1.Add();

	return 0;
}


The call to Add generates an error stating that the method is private which seems like the right behaviour. Is this the correct way to do this? Thanks. :) [Edited by - SeymourClearly on February 7, 2008 8:04:21 PM]
Advertisement
This works, but violates the LSP, so I would advise against using it. "Child" is not a "Parent" because a Parent can have its Add() and Remove() methods called in a sane manner, while a Child cannot.

The way I usually see the composite pattern applied is to use a root class which does not provide the Add() and Remove() methods (because at least one of its children does not have them).

That is:
class Tree { /* Abstract */ };class Node : public Tree { /* Add(Tree), Remove(Tree) */ };class Leaf : public Tree {};
Thanks, just what I needed. Also, does LSP stand for the Liskov Substitution Principle? I've no idea what that is but that's what a quick Google turns up. If that is what it is, I'll add it to the list of things I need to look at.

Thanks for the help. :)
Quote:The classes, Line, Rectangle and Text, all derive from Graphic but only implement the Draw method. This is where my question comes in. How do you properly go about hiding specific methods from the child, as it states above?


If you encounter this problem, your hierarchy is usually wrong.

In graphic/windowing hierarchies this happens somewhat frequently, and often is simply left unsolved to make API easier.

Think about it - Line is not a container, so Add or Remove have no purpose on it. Why does it behave like container? Making Line a component is generally not useful either.

If you're making a vector rendering application, this type of objects is usually better modeled as fly-weight.

General solution is to separate between component and container. Container extends component.

class Component {public:  virtual void paint( Canvas * c ) = 0;  Rect & getBounds();};class Text : public Component {};class Container : public Component {  void add(Component * c);  void remove(Component *c);  virtual void paint( Canvas * c );};


The rationale here is that container is a component itself, may be contained in other containers, and may render itself.

Component on the other hand is a leaf.
I think what you've said makes sense. I've not reached the discussion of the fly-weight pattern yet, but what you say seems to mostly make sense. Hopefully I'll understand it more fully when I get to that pattern.

Thanks, as always.
Sorry to necro this thread, but I have one more question. I believe the reason I had the pattern slightly wrong before, is down to the way the pattern is represented with diagrams. Here is an example.

My question is, why are the add(), remove() and getchild() methods listed under the abstract base class, Component, when they won't be used in the leaf? This is the same notation I came across in the Design Patterns: Elements of Reusable Object Oriented Software book and it's the reason I got mixed up in the first place.

I believe it should really be:

class Component{	virtual void Operation() = 0;};class Leaf : public Component{	void Operation();};class Composite : public Component{	void Operation();	void Add();	void Remove();	void GetChild();};


So, is there a purpose for this notation?

Cheers.
Quote:Original post by SeymourClearly
Sorry to necro this thread, but I have one more question. I believe the reason I had the pattern slightly wrong before, is down to the way the pattern is represented with diagrams. Here is an example.


That example is wrong for the purposes of demonstration or tutorial.

The add/remove there distract from design. All components there define add/remove functionality, where in general case that isn't so.

Try searching for "GoF composite".

The parent/child topological relation demonstrated in wikipedia is something else from inheritance structure. In wikipedia example, every component just happens to be a container as well. The add/remove is likely needed for implementation, but merely as a side effect of managing containment, not as a design requirement.

This is why tutorials are generally dangerous as learning material. And wikipedia is quite good as far as that goes, but wrong or misleading in this case.
Thanks (yet again), Antheus. The thing is, this is exactly the same way that the pattern is represented in diagramatic form in the book, which I think is why I had a little trouble understanding it.

Thanks for being patient with me and taking the time to answer my endless questions.

This topic is closed to new replies.

Advertisement