Explain composite design pattern.

0 votes
asked Apr 14, 2012 in Design Interview Questions by CareerMonk (17,460 points)
  

1 Answer

0 votes

A Composite is a tree structure consisting of individual objects mixed with compositions of objects, that is, objects that have other objects as their children. A classic example of this is a tree structure which we saw in Trees chapter. A tree consists of nodes and leaves. Leaves could represent a simple individual object, while nodes have branches with additional leaves and could for this reason described as collection of objects. The goal of the Composite pattern is to be able to treat individual objects (individual nodes or leaves) and compositions of objects (subtrees) the same way. All objects in the Composite are derived from Composite itself.

Another classic example is a file structure in an operating system (either Linux or Windows). As we know, any file system can have files, directories or combination of both. To begin with, we can create an interface for file-system object which just prints the name of it.

class FilesystemObject{

	    public:

	        virtual void print() = 0;

	};

To define the File object, we can simply create a concrete class for FilesystemObject and implement the print function.
class File : public FilesystemObject{

	    public:

	        File(const string& filename) : fileName(filename) {}

	        void print()    {

	            cout << fileName << endl;

	        }

	    private:

	        string fileName;

	};

Since directoy is a collection of file-system objects, we can use a list to store them. In the below code observe that, we have a list which stores FilesystemObjects.
class Directory : public FilesystemObject{

	    public:

	        Directory(const string& path) : dirPath(path) {}

	        ~ Directory()    {

	            for_each(children_.begin(), children_.end(), FilesystemObjectDeallocator() );

	        }

	        void print()    {

	            cout << "Printing Directoy File Names" << dirPath << endl;

	            for (list<FilesystemObject*>::iterator it = children_.begin(), itend = children_.end();

	                it != itend; ++it)

	                (*it)->print();

	        }

	        //Adds the object to the composition.

	        void add(FilesystemObject* object)    {

	            children_.push_back(object);

	        }

	        //Removes the object from the composition.

	        void remove(FilesystemObject* object)    {

	            list<FilesystemObject*>::iterator found = find(children_.begin(), children_.end(), object);

	            if( found != children_.end() )

	                children_.erase(found);

	        }

	    private:

	            list<FilesystemObject*> children_;   //Collection of children

	            string dirPath;

	            struct FilesystemObjectDeallocator    {

	                void operator()(FilesystemObject*& p) { delete p; p = 0; }

	            };

	    };

Sample client code for testing could be: note that we are adding directories as children to other directoy.
int main(int argc, char *argv[]){

	    File* file1 = new File("test1");

	    File* file2 = new File("test2");

	    File* file3 = new File("test3");

	

	    Directory* directory1 = new Directory("/root");

	    Directory* directory2 = new Directory("/username");

	    

	    //Compose the directories

	    directory2->add(file1);

	    directory2->add(file2);

	    directory1->add(file3);

	

	    directory1->add(directory2);

	    directory2->add(directory1);

	

	    directory1->print();

	

	    return 0;

	}

 

answered Apr 14, 2012 by CareerMonk (17,460 points)