Composite Pattern
Example
What is the Composite Pattern?
- Think of unpackaging Tree structures
Hierarchical Data
- Example of stored data in trees or other hierarchical structure
- Graphical Models
- Drawings
- Decision Trees
- Syntax Representations
The simple idea of the composite Pattern is to add operations to manage childern of the abstract base class
Both the composite and Leaf implements Component diagram, thus allowing the same operation on both objects but the important part is Composite Class which also contain the Component Objects which is symbolized by the black diamond indicating composition relationship between Composite and Component class.
Example using a Book Collection
Book Component
#ifndef BOOKCOMPONENT_H_
#define BOOKCOMPONENT_H_
class BookComponent
{
public:
BookComponent(){} // Constructor
virtual ~BookComponent(){} // Deconstructor
virtual void Add(BookComponent * newComponent){}
virtual void Remove(BookComponent * newComponent){}
virtual void DisplayInfo() = 0; // Pure Virtual Function, have to implement on our own
};
#endif
Book Header (Book.h)
#ifndef BOOK_H_
#define BOOK_H_
#include "BookComponent.h"
#include <string>
class Book : public BookComponent {
private:
std::string m_title;
std::string m_author;
public:
Book(std::string bookTitle, std::string author);
virtual ~Book();
virtual void DisplayInfo();
};
#endif
Book (Book.cpp)
#include "Book.h"
#include <iostream>
Book::Book(std::string title, std::string author): m_title(title), m_author(author){} // Constructor
Book::~Book(){} // Deconstructor
void Book::DisplayInfo()
{
// Print out Info
std::cout << "Book : " << m_title << " by " << m_author << "\n";
}
Book Group Header (BookGroup.h)
#ifndef BOOKGROUP_H_
#define BOOKGROUP_H_
#include "BookComponent.h"
#include <string>
#include <vector>
class BookGroup : public BookComponent {
private:
std::string m_groupName; // Name of Group
std::vector<BookComponent*> m_bookComponents; // Vector of Possible Components
public:
BookGroup(std::string groupName); // Constructor
virtual ~BookGroup(); // Deconstructor
virtual void Add(BookComponent * newComponent);
virtual void Remove(BookComponent * componentToRemove);
virtual void DisplayInfo();
};
#endif
Book Group (BookGroup.cpp)
#include "BookGroup.h"
#include <iostream>
BookGroup::BookGroup(std::string groupName) : m_groupName(groupName){} // Constructor
BookGroup::~BookGroup() {
std::vector<BookComponent *>::iterator pos;
for(pos = m_bookComponents.begin(); pos != m_bookComponents.end(); ++pos)
{
BookComponent * bookComponent = *pos;
delete bookComponent;
}
m_bookComponents.clear();
}
void BookGroup::Add(BookComponent * newComponent)
{
m_bookComponents.push_back(newComponent);
}
void BookGroup::Remove(BookComponent * componentToRemove)
{
std::vector<BookComponent *>::iterator pos;
for(pos = m_bookComponents.begin(); pos != m_bookComponents.end(); ++pos)
{
if(*pos == componentToRemove)
{
m_bookComponents.erase(pos);
break;
}
}
}
void BookGroup::DisplayInfo()
{
static std::string spaces;
std::cout << "Group : " << m_groupName << "\n";
spaces += std::string(" ");
std::vector<BookComponent *>::iterator pos;
for(pos = m_bookComponents.begin(); pos != m_bookComponents.end(); ++pos)
{
std::cout << spaces;
BookComponent * bookComponent = *pos;
bookComponent->DisplayInfo();
}
spaces.pop_back();
spaces.pop_back();
spaces.pop_back();
spaces.pop_back();
}
Client
Librarian Header File (Librarian.h)
#ifndef LIBRARIAN_H_
#define LIBRARIAN_H_
#include <string>
class BookComponent;
class Librarian {
public:
Librarian();
virtual ~Librarian();
void DisplayBookCollection();
private:
void BuildBookCollection();
// Different Groups
BookComponent * BuildFictionGroup();
BookComponent * BuildNonfictionGroup();
BookComponent * BuildKidsGroup();
BookComponent * BuildKidsAges3To5Group();
BookComponent * BuildBiographyGroup();
// Adding to Group of Random Books that is the Biden Crime Family
void AddBookToGroup(BookComponent * group, std::string bookTitle, std::string author);
BookComponent * m_pBookCollection;
};
#endif
Librarian (Librarian.cpp)
#include "Librarian.h"
#include "BookComponent.h"
#include "BookGroup.h"
#include "Book.h"
Librarian::Librarian(): m_pBookCollection(new BookGroup(std::string("Book Collection")))
{
BuildBookCollection(); // Build Collection
}
Librarian::~Librarian() {
delete m_pBookCollection;
}
void Librarian::BuildBookCollection()
{
AddBookToGroup(m_pBookCollection, std::string("Merriam-Webster's Collegiate Dictionary"), std::string("Merriam-Webster")); // Leaf!
m_pBookCollection->Add(BuildFictionGroup()); // Another Group
m_pBookCollection->Add(BuildNonfictionGroup()); // Another Group
}
// Two Main Groups
BookComponent * Librarian::BuildFictionGroup()
{
BookComponent * fictionGroup = new BookGroup(std::string("Fiction"));
fictionGroup->Add(BuildKidsGroup()); // Adding another group to this
return fictionGroup;
}
BookComponent * Librarian::BuildNonfictionGroup()
{
BookComponent * nonfictionGroup = new BookGroup(std::string("Nonfiction"));
nonfictionGroup->Add(BuildBiographyGroup()); // Adding another Group to this
return nonfictionGroup;
}
BookComponent * Librarian::BuildKidsGroup()
{
BookComponent * kidsGroup = new BookGroup(std::string("Kids"));
AddBookToGroup(kidsGroup, std::string("Green Eggs and Ham"), std::string("Dr. Suess"));
kidsGroup->Add(BuildKidsAges3To5Group());
return kidsGroup;
}
BookComponent * Librarian::BuildKidsAges3To5Group()
{
BookComponent * kidsAges3To5Group = new BookGroup(std::string("Kids (Ages 3-5)"));
AddBookToGroup(kidsAges3To5Group, std::string("Goodnight Moon"), std::string("Margaret Wise Brown"));
return kidsAges3To5Group;
}
BookComponent * Librarian::BuildBiographyGroup()
{
BookComponent * biographyGroup = new BookGroup(std::string("Biography"));
AddBookToGroup(biographyGroup, std::string("Steve Jobs"), std::string("Walter Isaacson"));
return biographyGroup;
}
void Librarian::AddBookToGroup(BookComponent * group, std::string bookTitle, std::string author)
{
BookComponent * book = new Book(bookTitle, author);
group->Add(book);
}
void Librarian::DisplayBookCollection()
{
m_pBookCollection->DisplayInfo();
}
Run our Program
#include "Librarian.h"
int main()
{
Librarian librarian;
librarian.DisplayBookCollection();
}
Output:
Group : Book Collection
Book : Merriam-Webster's Collegiate Dictionary by Merriam-Webster
Group : Fiction
Group : Kids
Book : Green Eggs and Ham by Dr. Suess
Group : Kids (Ages 3-5)
Book : Goodnight Moon by Margaret Wise Brown
Group : Nonfiction
Group : Biography
Book : Steve Jobs by Walter Isaacson
Another Example
Item Header File (Item.h)
#ifndef ITEM_H
#define ITEM_H
#include <string>
using std::string;
class Item {
protected:
string m_name;
double m_price;
public:
Item(string name, double price);
virtual ~Item();
//getters
virtual string getName() const{return m_name;}
virtual double getPrice() const{return m_price;}
virtual void print() const = 0;
};
#endif
Item (Item.cpp)
#include "Item.h"
Item::Item(string name, double price) {
m_name = name;
m_price = price;
}
Item::~Item() {}
Bundle Header (Bundle.h)
#ifndef BUNDLE_H
#define BUNDLE_H
#include "Item.h"
#include <vector>
#include <iostream>
#include <algorithm>
using std::vector;
using std::cout;
using std::endl;
class Bundle: public Item{
protected:
vector<Item*> m_items;
double m_discount_rate;
public:
Bundle(string name, double discount_rate);
// do stuff to delete things
virtual ~Bundle();
virtual void addItem(Item* item);
virtual void removeItem(Item* item);
virtual void print() const;
};
#endif
Bundle Function (Bundle.cpp)
#include "Bundle.h"
Bundle::Bundle(string name, double discount_rate): Item(name, 0) {
m_name = name;
m_discount_rate = discount_rate;
}
Bundle::~Bundle() {
}
void Bundle::addItem(Item* item){
m_items.push_back(item);
m_price += item->getPrice()*m_discount_rate;
}
void Bundle::removeItem(Item* item){
remove(m_items.begin(), m_items.end(), item);
m_price -= item ->getPrice()*m_discount_rate;
}
void Bundle::print() const{
cout << "Bundle " << m_name << " contains:(";
for(auto a: m_items){
a->print();
}
cout << ");" << "Bundle Price:" << m_price << ");";
}
Laptop Header (Laptop.h)
#ifndef LAPTOP_H
#define LAPTOP_H
#include "Item.h"
#include <string>
#include <iostream>
using std::string;
using std::cout;
class Laptop: public Item {
protected:
string m_speed;
public:
Laptop(string name, double price, string speed);
virtual ~Laptop();
virtual void print() const;
};
#endif /* LAPTOP_H */
Laptop File (Laptop.cpp)
#include "Laptop.h"
Laptop::Laptop(string name, double price, string speed):Item(name,price) {
m_speed = speed;
}
Laptop::~Laptop() {
}
void Laptop::print() const{
cout << "(Laptop " << "Name=" << m_name << ", Laptop Price=" << m_price << ");";
}
Phone Header (Phone.h)
#ifndef PHONE_H
#define PHONE_H
#include "Item.h"
#include <string>
#include <iostream>
using std::string;
using std::cout;
class Phone: public Item {
protected:
int m_year;
public:
Phone(string name, double price, int year);
virtual ~Phone();
virtual void print() const;
};
#endif
Phone File (Phone.cpp)
#include "Phone.h"
Phone::Phone(string name, double price, int year):Item(name,price) {
m_year = year;
}
Phone::~Phone() {
}
void Phone::print() const{
cout << "(Phone Name=" << m_name << ", Phone Price=" << m_price << ");";
}
Main File (main.cpp)
#include <cstdlib>
#include "Bundle.h"
#include "Item.h"
#include "Phone.h"
#include "Laptop.h"
using namespace std;
/*
*
*/
int main(int argc, char** argv) {
Bundle* b1=new Bundle("b1", 0.9);
b1->addItem(new Phone("iPhone 7", 1200, 2016));
b1->addItem(new Laptop("MacBook", 2800, "2.1GHZ"));
Bundle* b2=new Bundle("b2",0.8);
b2->addItem(new Phone("iPhone 6", 800, 2015));
b2->addItem(new Phone("iPhone 5", 500, 2015));
b2->addItem(new Phone("iPhone 4", 400, 2015));
Bundle* b3=new Bundle("b3", 1.0);
b3->addItem(b1);
b3->addItem(b2);
b3->print();
return 0;
}
Output:
Bundle b3 contains:(Bundle b1 contains:((Phone Name=iPhone 7, Phone Price=1200);(Laptop Name=MacBook, Laptop Price=2800););Bundle Price:3600);Bundle b2 contains:((Phone Name=iPhone 6, Phone Price=800);(Phone Name=iPhone 5, Phone Price=500);(Phone Name=iPhone 4, Phone Price=400););Bundle Price:1360););Bundle Price:4960);