Design Patterns

Composite



Design Pattern Type: Structural

GoF Statement of Intent:


Allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Brief Overview:



It allows a group of objects to be treated in the same way as a single instance of an object. The objective is to "compose" objects into tree structures to represent part-whole hierarchies. For example, if you had a drawing program in which you can resize an object. It would be convenient also to be able to select a group of objects in the drawing and call resize() on the entire group.
UML Diagram:

Discussion and In-class Example:

Take another look at the way the Iterator was used in the Waitress class. Using this technique Flo, the waitress could easily iterate through all the menus in either the Pancake House or the Cafe.
class Waitress
{
     private:
          PancakeHouseMenu *pancakeHouseMenu;
          CafeMenu *cafeMenu;
     public:
         Waitress(PancakeHouseMenu *phm, CafeMenu *cm)
          {
               this->pancakeHouseMenu=phm;
               this->cafeMenu = cm;
          }
          void printMenus()
          {
               MenuIterator *phIterator = pancakeHouseMenu->getIterator();
               Iterator *cIterator = cafeMenu->getIterator();
               cout << "MENU\n----\nBREAKFAST";
               printMenu(phIterator);
               cout << "MENU\n----\nLunch";
               printMenu(cIterator);
          }
          void printMenu(Iterator *itr)
          {
               while(itr->hasNext)
               {
                    MenuItem *menuItem = itr->next();
                    menuItem->print();
               }
          }
}
But now we run into a problem. In the example we used in class the Objectville Diner is now also going to merge with the other two, but the Diner keeps their MenuItems in a HashTable. We can create an iterator for a HashTable, but now we also have to modify the Waitress class to account for the new iterator.

A better solution is the Composite Pattern in which we make all menus subclasses of the Menu class and each has a getIterator() function. So we can modify the Waitress class, as shown below, so that it doesn't have to change when we add new Menu types. (Note: that the Waitress class now uses the vector from the Standard Template Library to store its Menu objects. Follow the STL link in the index on the left if you want to know more about the vector template.)
class Waitress
{
     private:
          vector menus;

     public:
         Waitress(vector menus)
          {
               this->menus = menus;
          }

          void printMenus()
          {
	    for(vector::iterator itr = menus.begin();
                          itr != menus.end(); itr++)
              {              
                   MenuIterator *mItr = itr->getIterator();
                   printMenu(mItr);
               }
          }

 	    void printMenu(MenuIterator *itr)
          {
               while(itr->hasNext)
               {
                    MenuItem *menuItem = itr->next();
                    menuItem->print();
               }
          }
}