Applying to Design Patterns
From Andrey
Revision as of 07:45, 3 February 2006 Andrey (Talk | contribs) ← Previous diff |
Current revision Andrey (Talk | contribs) |
||
Line 5: | Line 5: | ||
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. | 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. | ||
- | [[Image:Adapter.gif]] | + | [[Image:Adapter.gif|center]] |
- | 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. | + | 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. |
- | '''Example.''' A pluggable adapter without an intermediate class. | + | 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. | ||
<pre> | <pre> | ||
Line 32: | Line 34: | ||
This pattern deals with the situation when an object processing a request is not known to the object initiating a request. | This pattern deals with the situation when an object processing a request is not known to the object initiating a request. | ||
- | [[Image:Chain.gif]] | + | [[Image:Chain.gif|center]] |
- | 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 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 the need to process a new request (another button and so on) arises. 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 a special interface(s), and no need to create additional classes and protocols. | + | 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). |
- | Example. This is an example for an application SaveAs dialog. | + | 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. | ||
<pre> | <pre> | ||
Line 63: | Line 67: | ||
</pre> | </pre> | ||
- | Note, that the code of the intermediate class, saveas_dialog, remains free from processing and transporting the requests. It does not even know it participates in the Chain Of Responsibility. | + | 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 === | === Mediator === | ||
- | The Mediator pattern is used to decrease dependencies when objects work in a composition. When the objects manipulate each other, it has 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 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: |
- | [[Image:Mediator.gif]] | + | [[Image:Mediator.gif|center]] |
- | The Mediator pattern frees the classes of the participants from needing to know about each other. However, classes should 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 each other, but also free from knowing about their participation in the composition. | + | 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 bitmap), 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 1. 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 the 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 commonly-known 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. | + | '''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. |
- | [[Image:Form.jpg]] | + | [[Image:Form.gif|center]] |
<pre> | <pre> | ||
- | class dbform1 | + | class dbform |
{ | { | ||
- | db& data; | + | database& data; |
- | long current_index; | + | long current_index; |
- | dbitem current_item; | + | dbitem current_item; |
public: | public: | ||
- | listbox items; // list of items in stock | + | listbox items; // list of items in stock |
- | bitmap picture; // current item image | + | bitmap picture; // current item image |
- | editbox description; // current item description | + | editbox description; // current item description |
- | button apply; // apply changes button | + | button apply; // apply changes button |
- | // constructor | + | // constructor |
- | dbform1(db& d) : data(d) {} | + | dbform(database& d) : data(d) {} |
- | // listbox handlers | + | // listbox handlers |
- | void items::init() | + | void items::init() |
- | { | + | { |
- | items.source = data.list(); | + | items.source = data.list(); |
- | items::change(); | + | items::change(); |
- | } | + | } |
- | void items::change() | + | void items::change() |
- | { | + | { |
- | // selecting an item | + | // selecting an item |
- | current_index = items.selection(); | + | current_index = items.selection(); |
- | current_item = data(current_index); | + | current_item = data(current_index); |
- | description.update(); | + | description.update(); |
- | picture.update(); | + | picture.update(); |
- | apply.enable(false); | + | apply.enable(false); |
- | } | + | } |
- | // editbox handlers | + | // editbox handlers |
- | void description::update() | + | void description::update() |
- | { | + | { |
- | description.set(current_item.description); | + | description.set(current_item.description); |
- | } | + | } |
- | void description::changed() | + | void description::changed() |
- | { | + | { |
- | apply.enable(true); | + | apply.enable(true); |
- | } | + | } |
- | // bitmap handler | + | // bitmap handler |
- | void picture::update() | + | void picture::update() |
- | { | + | { |
- | picture.set(current_item.picture); | + | picture.set(current_item.picture); |
- | picture.redraw(); | + | picture.redraw(); |
- | } | + | } |
- | + | ||
- | // button handler | + | // button handler |
- | // called when user presses the button | + | // called when user presses the button |
- | void apply::press() | + | void apply::press() |
- | { | + | { |
- | current_item.description = description.get(); | + | current_item.description = description.get(); |
- | data.set(current_index, current_item); | + | data.set(current_index, current_item); |
- | } | + | } |
}; | }; | ||
</pre> | </pre> |
Current 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.
// 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); } };