Applying to Design Patterns

From Andrey

Revision as of 03:10, 4 February 2006; view current revision
←Older revision | Newer revision→

Overriding in owner class can also be applied to the design patterns described in [2]. Moreover, it can simplify the applications of some patterns.

Pluggable adapter

The Pluggable Adapter pattern is used when code requires a special interface from an object. To provide such an interface, a class called adapter is created and passed to the code.

Client code works with an abstract adapter, defining the interface. The actual adapter is an instance of a concrete adapter class, working with the particular class it adapts. Process of adaptation consists of creation of the concrete adapter class, overriding the functions of abstract adapter, and giving an instance of a concrete adapter to the client code.

Overriding in owner class allows doing the adaptation without creating a concrete adapter class and an object.

Example. A pluggable adapter without need for an intermediate class. Look ma, no hands.

// abstract adapter
class interview_adapter
{ 
public: 
	virtual answer ask(question q) =0; 
};

// class to adapt – no concrete adapters
class person
{
public:
	interview_adapter interviewee;
	answer interviewee::ask(question q) { return think_hard(q); }
};

Chain Of Responsibility

This pattern deals with the situation when an object processing a request is not known to the object initiating a request.

Suppose the user has pressed a dialog button, such as a help button, but the knowledge about what to do is not in the button class, and not in its owner, the dialog, but is in the class which owns the dialog - the application's main window. The object ‘main window’ is not known to the object ‘button’. To transmit the request to the processing object, Chain Of Responsibility uses inheritance: we make the button, the dialog, and the main window subclasses of a class that is able to process the request (for instance, of the class HelpHandler with a virtual function Help()). When the request comes, the button uses the default implementation of the handler, which redirects the request to the owner (owner->Help()), and it is repeated until the request reaches an object capable of processing it. To apply the Chain Of Responsibility pattern, we have to make the classes in the chain a) share an interface for processing a particular request, and b) know about the owner object. Such requirements mean that the Chain Of Responsibility can be applied only if all its participants were designed to participate in it. It will be necessary to modify all the classes in the chain if we need to process a new type of request (e.g. another button).

Overriding in owner class can help eliminate these drawbacks. Indeed, it is a vehicle for transferring the request from the initiator to the acceptor with no need for the participants to implement any interface(s), and with no need to create additional classes and protocols.

Example. An example of an application SaveAs dialog.

class button 
{ 
public: 
	virtual void press() =0; 
};

class saveas_dialog
{
public:
	string new_name;
	button OK;
	button Help;
};

class main_window
{
public:
	saveas_dialog saveas;

	saveas::OK::press() { save(active_document(), saveas.new_name); }
	saveas::Help::press() { run_help(HLP_SAVEAS); }
};

Note, that the code of the intermediate class, saveas_dialog, remains free from processing and transporting the requests. It does not even have to know it participates in the Chain Of Responsibility.

Mediator

The Mediator pattern is used to decrease dependencies when objects work in a composition. When the objects have to manipulate each other, it may have a negative impact on the reusability of their code. It also complicates the modifications of the composition, “because its behavior is distributed among objects” [2]. As a solution, a mediator object is created, which encapsulates collective behavior, removing the need for the parts of the composition to directly ‘talk’ to each other. All the requests come from and go to the mediator object:

The Mediator pattern frees the classes of the participants from needing to know about each other. However, classes still have to know about the mediator class, and how to deal with the mediator object. Applying overriding in owner class to the Mediator pattern lets us to avoid the creation of: a) a special mediator class; b) a run-time mediator object; and c) a protocol participant-to-mediator. This leaves the code of the participant classes not only free from having to know about each other, but also free from knowing about their participation in the composition.

Example. Consider a form displaying database records, consisting of a list of items in the database (items listbox), a picture representing a currently selected item (picture image), a description of the item (description editbox). The user can edit the description. When the user finishes editing, he/she presses the apply button to enter the data in the database. The behavior of the parts in the form should be coordinated: picture and description must refresh when the current item selection changes; the apply button should be disabled until the user changes the item description, after pressing apply, the changes should be written to the database. These interactions are shown in the figure below. The arrows show how the objects influence each other. Underlined objects are the ones that are visible to the user. In the Mediator pattern, we need to create an object that accepts requests from the participants, processes them and issues requests/updates to other participants. Overriding in owner class will allow us to eliminate the mediator class, the mediator object, and the mediator protocol by overriding member functions of the participants in the composition class. The classes of the participants will not have to support the protocol of mutual requests and updates. They will not have to deal with some designated object to send the notifications to. Instead, the owner class will take care of all the traits in the behavior of its components specific for the work in the form.

class dbform
{
	database& data;
	long current_index;
	dbitem current_item;

public:
	listbox items; // list of items in stock
	bitmap picture; // current item image
	editbox description; // current item description
	button apply; // apply changes button

	// constructor
	dbform(database& d) : data(d) {} 

	// listbox handlers
	void items::init() 
	{ 
		items.source = data.list();
		items::change();
	}

	void items::change() 
	{ 
		// selecting an item
		current_index = items.selection(); 
		current_item = data(current_index); 
		description.update();
		picture.update(); 
		apply.enable(false);
	}

	// editbox handlers
	void description::update() 
	{ 
		description.set(current_item.description); 
	}

	void description::changed() 
	{ 
		apply.enable(true); 
	}

	// bitmap handler
	void picture::update() 
	{ 
		picture.set(current_item.picture); 
		picture.redraw(); 
	}
	
	// button handler
	// called when user presses the button
	void apply::press()
	{ 
		current_item.description = description.get(); 
		data.set(current_index, current_item); 
	} 
};


Next: Discussion of Alternatives