list<string>::iterator pos;
for (pos = list.begin(); pos != list.end(); ++pos)
{
string item = *pos;
. . .
}
Node* current = list.head;
while (current != NULL)
{
string item = current->data;
current = current->next;
. . .
}
Consider the interface:
A stack has:
|
A array structure w/random access has:
|
T get() const // Get element at cursor void set(const T& t) // Set element at cursor to t T remove() // Remove element at cursor void insert(const T& t) // Insert t before cursor void reset() // Reset cursor to head void next() // Advance cursor bool is_done() // Check if cursor can be advanced
for (list.reset(); !list.is_done(); list.next())
{
T item = list.get();
. . .
}
| Name in Design Pattern | Actual Name (List Iterators) |
|---|---|
| Aggregate | list<T> |
| Iterator | list<T>::iterator |
| create_iterator() | begin(), end() |
| next() | ++ operator |
| is_done | Test against end() |
| current_item() | * operator |
Input streams as example of iterator pattern:
| Name in Design Pattern | Actual Name (Input Streams) |
|---|---|
| Aggregate | Source of Bytes |
| Iterator | istream |
| create_iterator() | open() |
| next() | get() |
| is_done() | ! fail() |
| current_item() | Return value of get() |
Generic Programming with Inheritance and Templates
void print_all(AbstractAggregate* items)
{
AbstractIterator* iter = items->create_iterator();
while (!iter->is_done())
{
cout << iter->current_item() << "\n";
iter->next();
}
}
template<typename T>
void print_all(const T& items)
{
T::iterator iter = items.begin();
while (iter != items.end())
{
cout << *iter << "\n";
iter++;
}
}
string s = "235";
istringstream istr(s);
int n;
s >> n; // Now n is the integer 235
| Name in Design Pattern | Actual Name (String Streams) |
|---|---|
| Adaptee | string |
| Target | istream |
| Adapter | istringstream |
| Client | Code that wants to use operator>> to read string |
| target_function() | get() |
| adaptee_function | operator[] |
copy( source.begin(), source.end(),ostream_iterator<string> ( cout, "\n" ));
copy(istream_iterator<string>(cin), istream_iterator<string>(), target.begin() );
| Name in Design Pattern | Actual Name (String Streams) |
|---|---|
| Adaptee | istream, ostream |
| Target | iterator |
| Adapter | istream_iterator, ostream_iterator |
| Client | Code that wants to use a stream |
| target_function() | <<, >>, fail() |
| adaptee_function | The *, =, +, != operators |
Example - stream buffers
Example - stream buffers (cont.)
cout << "Hello, World!\n";
Example - stream buffers (cont.)
ostream& ostream::operator<<(const char s[])
{
int i = 0;
while (s[i] != '\0')
{
rdbuf()->sputc(s[i]); // returns a ptr to the buffer
i++;
}
return *this;
}
Example - stream buffers (cont.)
int streambuf::sputc(char c)
{
if (buffer full )
return overflow(c);
else
{
add c to the buffer
return c;
}
}
Example - stream buffers (cont.)
|
| Name in Design Pattern | Actual Name (Stream Buffers) |
|---|---|
| AbstractClass | streambuf |
| ConcreteClass | filebuf, stringbuf |
| template_method() | sputc |
| primitive_op1 | overflow |
vector<string> names;
. . .
sort(names.begin(), names.end() );
vector<Employee> staff;
sort(staff.begin(), staff.end() );
bool operator<(const Employee& a, const Employee& b)
{
return a.get_name() < b.get_name();
}
SalaryComparator comp;
sort(staff.begin(), staff.end(), comp);
sort(staff.begin(), staff.end(), SalaryComparator() );
class SalaryComparator
{
public:
bool operator()(const Employee& a, const Employee& b)
{
return a.get_salary() < b.get_salary();
}
};
if (!comp(x, y)) rearrange x and y;
class SalaryComparator
{
public:
SalaryComparator(bool a) : ascending(a) {}
bool operator()(const Employee& a, const Employee& b)
{
if (ascending)
return a.get_salary() < b.get_salary();
else
return a.get_salary() > b.get_salary();
}
private:
bool ascending;
}
SalaryComparator reverse_comp(false);to sort by decreasing salary
sort(staff.begin(), staff.end(), SalaryComparator() ):
|
| Name in Design Pattern | Actual Name (String Streams) |
|---|---|
| Context | sort function |
| Strategy | The i/f that sort expects comparator to have, i.e., operator() |
| ConcreteStrategy | The comparator class |
| do_work() | The operator() |
class Bundle : public Item
{
. . .
private:
vector<Item*> items;
};
double Bundle::get_unit_price() const
{
double price = 0;
for (int i = 0; i < items.size(); i++)
{
price = price +
( items[i]->get_unit_price()
* items[i]->get_quantity() );
}
return price;
}
| Name in Design Pattern | Actual Name (String Streams) |
|---|---|
| Primitive | Item |
| Composite | Bundle |
| op() | get_unit_price |
class Item
{
public:
virtual double get_unit_price() const = 0;
virtual int get_quantity() const = 0;
virtual string get_description() const = 0;
double get_total_price() const;
. . .
};
double Item::get_total_price() const
{
return get_quantity() * get_unit_price();
}
class ProductItem : public Item
{
public:
ProductItem(Product p);
virtual double get_unit_price() const;
virtual string get_description() const;
private:
Product prod;
};
double ProductItem::get_unit_price() const
{
return prod.get_price();
}
class Invoice
{
public:
void add(Item* item)
{
items.push_back(item);
}
. . .
private:
vector<Item*> items;
};
class ItemIterator
{
public:
. . .
bool is_done() const;
void next();
Item* get() const;
private:
vector<Item*>& items;
int pos;
};
Item* ItemIterator::get() const
{
if (pos < items.size())
return items[pos];
else
return NULL;
}
inline void ItemIterator::next()
{
pos++;
}
bool ItemIterator::is_done() const
{
return pos >= items.size();
}
class InvoicePrinter
{
public:
virtual void print_header(string s) = 0;
virtual void print_string(string value, bool pad_right) = 0;
virtual void print_number(double value, int precision) = 0;
virtual void print_footer(string s, double total) = 0;
};
void Invoice::print(InvoiceFormatter& formatter)
{
print_header("I N V O I C E");
print_string("Description", true);
print_string("Unit Price", false);
print_string("Qty", false);
print_string("Total Price", false);
double amount_due = 0;
for (ItemIterator iter = inv.create_iterator();
!iter.is_done(); iter.next())
{
Item* it = iter.get();
print_string(it->get_description(), true);
print_number(it->get_unit_price(), 2);
print_number(it->get_quantity(), 0);
print_number(it->get_total_price(), 2);
amount_due = amount_due + it->get_total_price();
}
print_footer("AMOUNT DUE:", amount_due);
}
Quick table of patterns from Design Patterns:
| Pattern Name | Description | Example |
|---|---|---|
| Abstract Factory | An abstract class defines methods that construct related products. Concrete factories create these product sets | An abstract class specifies methods for constructing buttons, methods, and so on. Each user interface look and feel supplies a concrete subclass |
| Bridge | An abstraction and its implementation have separate inheritance hierarchies | A hierarchy of window types has separate implementations in various operating systems |
| Builder | A builder class has methods to build parts of a complex product, and to retrieve the completed product | A document builder has methods to build paragraphs, tables, and so on |
| Pattern Name | Description | Example |
|---|---|---|
| Chain of Responsibility | A request is passed to the first handler in a chain. Each handler acts on the request (or chooses not to act), and passes the request on to the next handler | An event-handling mechanism passes a mouse or key event to a component, which then passes it to the parent component |
| Command | Commands are implemented as objects | A word processor stores recently issued commands so that they can be undone |
| Decorator | The behavior of a class is enhanced, keeping its interface | A user interface component is decorated with scroll bars or borders |
| Facade | A complex subsystem is accessed through a single class | A driver class provides access to the functionality of a database system |
| Pattern Name | Description | Example |
|---|---|---|
| Factory Method | A virtual constructor can be redefined by derived classes | Collection classes that derive from a common base class redefine the create_iterator function |
| Flyweight | Uses shared objects instead of large numbers of separate objects with identical state | A word processor uses shared objects for styled characters rather than a separate object for each character |
| Interpreter | A class hierarchy represents grammar rules. The interpreter recursively evaluates a parse tree of rule objects | A program interactively evaluates mathematical expressions by building and evaluating a parse tree |
| Pattern Name | Description | Example |
|---|---|---|
| Mediator | An object encapsulates the interaction of other objects | All components in a dialog box notify a mediator of state changes. The mediator updates affected components |
| Memento | An object yields an opaque snapshot of a part of its state, and can later return its state from that snapshot | An undo mechanism requests a memento from an object before mutating it. If the operation is undone, the memento is used to roll the object back to its old state |
| Observer | An object wants to be notified when another object generates an event | User interface components generate events, such as button clicks and text changes. A dialog box observes the events and repaints its contents |
| Pattern Name | Description | Example |
|---|---|---|
| Proxy | A service needs to be made more versatile without affecting the service provider or client | A proxy class sends client requests to a server object on a different computer |
| Singleton | All clients need access to a single shared object of a class | A random number singleton gives all clients access to the same generator |
| State | A separate object is used for each state. State-dependent code is distributed over the various state classes | An image editor has different drawing states. Each state is handled by a separate tool object |
| Visitor | A structure with a fixed set of element classes needs an extensible set of operations | An XML visitor visits a tree of XML elements, applying arbitrary operations to each node |