Applications of Overriding in Owner
From Andrey
Revision as of 06:12, 3 February 2006 Andrey (Talk | contribs) ← Previous diff |
Current revision Andrey (Talk | contribs) |
||
Line 1: | Line 1: | ||
Overriding in owner class can be helpful in the following situations: composing objects for collective work; customizing an object’s behavior for a particular use; propagating requests from components to aggregates; implementing control over manipulations on class members; and others. | Overriding in owner class can be helpful in the following situations: composing objects for collective work; customizing an object’s behavior for a particular use; propagating requests from components to aggregates; implementing control over manipulations on class members; and others. | ||
We will consider the application of overriding in owner class to the following problems: | We will consider the application of overriding in owner class to the following problems: | ||
- | #The problem of dependencies. | ||
- | #The problem of notifications. | ||
- | #Code reusability. | ||
- | …and to design cases: | ||
- | #Exporting a member in the interface. | ||
- | #Implementing the concept of a property in C++. | ||
- | 1. Problem of Dependencies. Consider class F with members a and b, belonging to classes A and B respectively. Code C1 works with class B. When changes happen to F::b, F::a must be changed. This means that B, otherwise not tied with A, must know about A's existence to send a notification (the knowledge about A is brought into B, introducing member-to-member dependency). Alternatively, B must know about F's existence, to get F to notify F::a (the knowledge about F is brought into B, introducing member-to-owner dependency). The third solution is to rewrite code C1 to work with F instead of B (the knowledge about F is brought in C1, the interface of B or its part is included in the interface of F). | + | == Problem of Dependencies == |
+ | Consider class F with members a and b, belonging to classes A and B respectively. Code C1 works with class B. When changes happen to F::b, F::a must be changed. This means that B, otherwise not tied with A, must know about A's existence to send a notification (the knowledge about A is brought into B, introducing member-to-member dependency). Alternatively, B must know about F's existence, to get F to notify F::a (the knowledge about F is brought into B, introducing member-to-owner dependency). The third solution is to rewrite code C1 to work with F instead of B (the knowledge about F is brought in C1, the interface of B or its part is included in the interface of F). | ||
- | [[Image:1.gif]] | + | [[Image:over1.gif|center]] |
+ | |||
+ | '''Problem:''' Developing a system from components, one has to make the code of some components depend on the existence, interface, and/or behavior of others. | ||
+ | |||
+ | Overriding in owner class (overriding methods of F::b in F) allows one to leave the code of B and C1 free from the need to know about owners and neighbors. It lets B remain simpler and more reusable, and leaves C1 unchanged. The details specific to F stay where they should: in class F. | ||
+ | |||
+ | <div id="Visual_cash"></div> | ||
+ | |||
+ | '''Example.''' The ''visual_cash'' class is a screen representation of money, made of two parts: ''cash'', providing data for the financial algorithms, and ''editbox'', reflecting changes made by the algorithms on screen and letting the user edit the amounts. Problem: changes made to one part must be reflected in the other. How to write these classes? | ||
+ | |||
+ | <pre> | ||
+ | class visual_cash | ||
+ | { | ||
+ | public: | ||
+ | cash value; // data | ||
+ | editbox face; // representation | ||
+ | |||
+ | // overriden functions connect | ||
+ | // data with representation | ||
+ | |||
+ | void value::set(float l) | ||
+ | { | ||
+ | value.cash::set(l); | ||
+ | face.set_text(value.get()); | ||
+ | } | ||
+ | |||
+ | void face::init() | ||
+ | { | ||
+ | face.set_text(value.get()); | ||
+ | } | ||
+ | |||
+ | void face::changed() | ||
+ | { | ||
+ | // when the user changes the text | ||
+ | // in the widget, value is updated. | ||
+ | value.cash::set(to_float(face.get_text())); | ||
+ | } | ||
+ | } visual_money; | ||
+ | |||
+ | |||
+ | // Pass visual_money.value to an algotithm, | ||
+ | // and if it is changed, the display is updated. | ||
+ | invest(&visual_money.value); | ||
+ | </pre> | ||
+ | |||
+ | The components of ''visual_cash'' class are coordinated, however classes ''editbox'', ''cash'' and the algorithms like ''create_profit()'' remain free from the need to know about each other. The composition-specific peculiarities remain in the composition class. | ||
+ | |||
+ | == Problem of Notifications == | ||
+ | |||
+ | There is a class B with a member a, belonging to class A. Code C1 works with objects of class A, in particular with the member of B. B should know what C1 does to B::a. Thus A should notify B about the operations performed on itself, or C1 should be changed to notify B about them. | ||
+ | |||
+ | [[Image:over2.gif|center]] | ||
+ | |||
+ | '''Problem:''' The code that makes changes must issue notifications about them. | ||
+ | |||
+ | Overriding in owner class (overriding methods of B::a in B) allows to one leave the code of A and C1 free from sending notifications to B about what happens to B::a. The code of A remains simpler and more reusable, the code C1 does not change. | ||
+ | |||
+ | <div id="Working_girl"></div> | ||
+ | |||
+ | '''Example.''' Helen has a pager. She has to know about incoming calls. | ||
+ | |||
+ | <pre> | ||
+ | // pager class | ||
+ | class motorola2sx | ||
+ | { | ||
+ | public: | ||
+ | virtual void call(); | ||
+ | }; | ||
+ | |||
+ | class working_girl | ||
+ | { | ||
+ | public: | ||
+ | motorola2sx pager; | ||
+ | |||
+ | // making owner know about call | ||
+ | void pager::call() { callback(); } | ||
+ | } Helen; | ||
+ | |||
+ | // calling Helen | ||
+ | Helen.pager.call(); | ||
+ | </pre> | ||
+ | |||
+ | In this example, thanks to using overriding in owner class, notifications are completely absent. Reacting to the incoming calls is the responsibility of the pager owner, and, according to this, the code reacting to calls resides in the owner class. The ''motorola2sx'' class is focused on solving its own problems. The vendor of ''motorola2sx'' does not have to take care of the need to notify somebody about the calls. | ||
+ | |||
+ | == Code Reusability == | ||
+ | |||
+ | Class A is adapted for work with B, but the tight coupling to the interface and behavior of B makes it hard to adapt A for work with C. | ||
+ | |||
+ | [[Image:over3.gif|center]] | ||
+ | |||
+ | '''Problem:''' The peculiarities specific to how an object's works with other objects find themselves in the object's class and make the latter less reusable. | ||
+ | |||
+ | With the help of overriding in owner class, the code specific to the role played by B::a in B can be placed in class B, with the result of improving A's reusability. | ||
+ | |||
+ | <div id="Circular_gauge"></div> | ||
+ | |||
+ | '''Example.''' This is an example of a circular gauge that can be used in various devices: | ||
+ | |||
+ | <pre> | ||
+ | class circular_gauge | ||
+ | { | ||
+ | public: | ||
+ | virtual float get_data() =0; // called by draw() to display current value | ||
+ | void draw(); | ||
+ | } | ||
+ | |||
+ | class voltmeter | ||
+ | { | ||
+ | public: | ||
+ | circular_gauge face; | ||
+ | float face::get_data() { return voltage(schema, loc); } | ||
+ | }; | ||
+ | |||
+ | class compass | ||
+ | { | ||
+ | public: | ||
+ | circular_gauge face; | ||
+ | float face::get_data() { return azimuth(); } | ||
+ | }; | ||
+ | |||
+ | class speedometer | ||
+ | { | ||
+ | public: | ||
+ | circular_gauge face; | ||
+ | float face::get_data() { return weel[0].rotationspeed() * weel[0].radius; } | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | Because the ''circular_gauge'' class does not contain code specific to the various devices and does not use special protocols, like notifications, it is well suited for use with any of them. Note that even though ''circular_gauge'' has a pure virtual method, in the presence of a new overriding mechanism it is a complete, ‘off the shelf’, ready to use component. | ||
+ | |||
+ | == Exporting a Member in the Interface == | ||
+ | |||
+ | I want to let the client of B apply code C1 to B's member B::a, but B is not aware of the changes C1 can make. | ||
+ | |||
+ | [[Image:over4.gif|center]] | ||
+ | |||
+ | '''Problem:''' Exporting (making public) class members in C++ is complicated by the absence of mechanisms allowing a class to control what happens to its members. | ||
+ | |||
+ | Currently the necessary job is done by adding new classes and dependencies to the code. For example, one solution is to make B::a belonging to a class A1, inheriting from class A extended to notify B. It has a number of drawbacks, which are discussed later in detail in the “Comparison with Alternatives” section. Overriding in owner class (overriding of the methods of B::a in B) lets B control its members without resorting to subclassing or introducing new dependencies. | ||
+ | |||
+ | <div id="Text_editor"></div> | ||
+ | |||
+ | '''Example.''' Consider an ''editor'' having a ''selected_text'' having a ''font''. When changing the ''font'', it is necessary for the ''editor'' to ''update_display()''. We can do it by overriding the methods of the ''font'' member of the ''editor'' class’s ''selected_text'' member. | ||
+ | |||
+ | <pre> | ||
+ | class text_font | ||
+ | { | ||
+ | int size; | ||
+ | public: | ||
+ | virtual void set_size(int i){ size = i; } | ||
+ | }; | ||
+ | |||
+ | class text_run | ||
+ | { | ||
+ | public: | ||
+ | text_font font; | ||
+ | }; | ||
+ | |||
+ | class editor_app | ||
+ | { | ||
+ | text_run all_text; | ||
+ | public: | ||
+ | text_run selected_text; | ||
+ | |||
+ | virtual void selected_text::font::set_size(int i) | ||
+ | { | ||
+ | // call the original | ||
+ | selected_text.font.text_font::set_size(i); | ||
+ | // draw changes | ||
+ | update_display(selected_text); | ||
+ | } | ||
+ | } editor; | ||
+ | |||
+ | void some_func(text_run& text) | ||
+ | { | ||
+ | text.font.set_size(default_size); | ||
+ | } | ||
+ | |||
+ | // see changes reflecting on the screen | ||
+ | some_func(editor.selected_text); | ||
+ | </pre> | ||
+ | |||
+ | In this example, the algorithms manipulating the text (''some_func'') need not know about the classes that depend on the changes. The code of ''text_run'' does not have to transmit requests from the receiving object (''text_run::font'') to the object that participates in their handling (''editor''), because it is taken care of by overriding in owner class mechanism. Note that the class ''text_run'' is not required to override functions of its member ''font''. | ||
+ | |||
+ | == Implementing the Concept of Property == | ||
+ | |||
+ | I want to interpret B::a not simply as a member of B, but as its ''property''. In other words, when one requests the value of B::a, I want some function to get called. | ||
+ | |||
+ | '''Problem:''' There are no mechanisms in C++ allowing hiding a function behind a member's name. | ||
+ | |||
+ | Here is how we can do something like a property with standard C++: | ||
+ | |||
+ | <pre> | ||
+ | // abstract property class | ||
+ | class int_property | ||
+ | { | ||
+ | public: | ||
+ | virtual operator int() =0; | ||
+ | virtual int operator =(int i) =0; | ||
+ | }; | ||
+ | |||
+ | class window; | ||
+ | |||
+ | // concrete property class | ||
+ | class window_width : public int_property | ||
+ | { | ||
+ | public: | ||
+ | window *owner; | ||
+ | operator int(); | ||
+ | int operator =(int i); | ||
+ | }; | ||
+ | |||
+ | class window | ||
+ | { | ||
+ | public: | ||
+ | window_width width; | ||
+ | }; | ||
+ | |||
+ | // implementations | ||
+ | window_width::operator int() const { return ::get_window_width(owner); } | ||
+ | int window_width::operator =(int i) { ::set_window_width(owner, i); } | ||
+ | </pre> | ||
+ | |||
+ | The ''window'' class exports an object of the class ''window_width'' in its interface. To begin with, there are disadvantages caused by exporting a member. Secondly, because ''window_width'' knows the interface of the owner class, the approach has the disadvantages described above in “The Problem of Dependencies”. Thirdly, the class ''window_width'' is absolutely non-reusable. | ||
+ | |||
+ | <div id="Int_property"></div> | ||
+ | |||
+ | Overriding in owner class allows us make a more elegant implementation of the property concept: | ||
+ | |||
+ | <pre> | ||
+ | // an abstract class for all integer properties | ||
+ | class int_property | ||
+ | { | ||
+ | public: | ||
+ | virtual operator int() =0; | ||
+ | virtual int operator =(int i) =0; | ||
+ | }; | ||
+ | |||
+ | class window | ||
+ | { | ||
+ | public: | ||
+ | int_property width; | ||
+ | |||
+ | // a property customized for the window class | ||
+ | width::operator int() const { return ::get_window_width(this); }; | ||
+ | int width::operator =(int i) { ::set_window_width(this, i); } ; | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | '''Next: [[Applying to Design Patterns]]''' |
Current revision
Overriding in owner class can be helpful in the following situations: composing objects for collective work; customizing an object’s behavior for a particular use; propagating requests from components to aggregates; implementing control over manipulations on class members; and others. We will consider the application of overriding in owner class to the following problems:
Contents |
Problem of Dependencies
Consider class F with members a and b, belonging to classes A and B respectively. Code C1 works with class B. When changes happen to F::b, F::a must be changed. This means that B, otherwise not tied with A, must know about A's existence to send a notification (the knowledge about A is brought into B, introducing member-to-member dependency). Alternatively, B must know about F's existence, to get F to notify F::a (the knowledge about F is brought into B, introducing member-to-owner dependency). The third solution is to rewrite code C1 to work with F instead of B (the knowledge about F is brought in C1, the interface of B or its part is included in the interface of F).
Problem: Developing a system from components, one has to make the code of some components depend on the existence, interface, and/or behavior of others.
Overriding in owner class (overriding methods of F::b in F) allows one to leave the code of B and C1 free from the need to know about owners and neighbors. It lets B remain simpler and more reusable, and leaves C1 unchanged. The details specific to F stay where they should: in class F.
Example. The visual_cash class is a screen representation of money, made of two parts: cash, providing data for the financial algorithms, and editbox, reflecting changes made by the algorithms on screen and letting the user edit the amounts. Problem: changes made to one part must be reflected in the other. How to write these classes?
class visual_cash { public: cash value; // data editbox face; // representation // overriden functions connect // data with representation void value::set(float l) { value.cash::set(l); face.set_text(value.get()); } void face::init() { face.set_text(value.get()); } void face::changed() { // when the user changes the text // in the widget, value is updated. value.cash::set(to_float(face.get_text())); } } visual_money; // Pass visual_money.value to an algotithm, // and if it is changed, the display is updated. invest(&visual_money.value);
The components of visual_cash class are coordinated, however classes editbox, cash and the algorithms like create_profit() remain free from the need to know about each other. The composition-specific peculiarities remain in the composition class.
Problem of Notifications
There is a class B with a member a, belonging to class A. Code C1 works with objects of class A, in particular with the member of B. B should know what C1 does to B::a. Thus A should notify B about the operations performed on itself, or C1 should be changed to notify B about them.
Problem: The code that makes changes must issue notifications about them.
Overriding in owner class (overriding methods of B::a in B) allows to one leave the code of A and C1 free from sending notifications to B about what happens to B::a. The code of A remains simpler and more reusable, the code C1 does not change.
Example. Helen has a pager. She has to know about incoming calls.
// pager class class motorola2sx { public: virtual void call(); }; class working_girl { public: motorola2sx pager; // making owner know about call void pager::call() { callback(); } } Helen; // calling Helen Helen.pager.call();
In this example, thanks to using overriding in owner class, notifications are completely absent. Reacting to the incoming calls is the responsibility of the pager owner, and, according to this, the code reacting to calls resides in the owner class. The motorola2sx class is focused on solving its own problems. The vendor of motorola2sx does not have to take care of the need to notify somebody about the calls.
Code Reusability
Class A is adapted for work with B, but the tight coupling to the interface and behavior of B makes it hard to adapt A for work with C.
Problem: The peculiarities specific to how an object's works with other objects find themselves in the object's class and make the latter less reusable.
With the help of overriding in owner class, the code specific to the role played by B::a in B can be placed in class B, with the result of improving A's reusability.
Example. This is an example of a circular gauge that can be used in various devices:
class circular_gauge { public: virtual float get_data() =0; // called by draw() to display current value void draw(); } class voltmeter { public: circular_gauge face; float face::get_data() { return voltage(schema, loc); } }; class compass { public: circular_gauge face; float face::get_data() { return azimuth(); } }; class speedometer { public: circular_gauge face; float face::get_data() { return weel[0].rotationspeed() * weel[0].radius; } };
Because the circular_gauge class does not contain code specific to the various devices and does not use special protocols, like notifications, it is well suited for use with any of them. Note that even though circular_gauge has a pure virtual method, in the presence of a new overriding mechanism it is a complete, ‘off the shelf’, ready to use component.
Exporting a Member in the Interface
I want to let the client of B apply code C1 to B's member B::a, but B is not aware of the changes C1 can make.
Problem: Exporting (making public) class members in C++ is complicated by the absence of mechanisms allowing a class to control what happens to its members.
Currently the necessary job is done by adding new classes and dependencies to the code. For example, one solution is to make B::a belonging to a class A1, inheriting from class A extended to notify B. It has a number of drawbacks, which are discussed later in detail in the “Comparison with Alternatives” section. Overriding in owner class (overriding of the methods of B::a in B) lets B control its members without resorting to subclassing or introducing new dependencies.
Example. Consider an editor having a selected_text having a font. When changing the font, it is necessary for the editor to update_display(). We can do it by overriding the methods of the font member of the editor class’s selected_text member.
class text_font { int size; public: virtual void set_size(int i){ size = i; } }; class text_run { public: text_font font; }; class editor_app { text_run all_text; public: text_run selected_text; virtual void selected_text::font::set_size(int i) { // call the original selected_text.font.text_font::set_size(i); // draw changes update_display(selected_text); } } editor; void some_func(text_run& text) { text.font.set_size(default_size); } // see changes reflecting on the screen some_func(editor.selected_text);
In this example, the algorithms manipulating the text (some_func) need not know about the classes that depend on the changes. The code of text_run does not have to transmit requests from the receiving object (text_run::font) to the object that participates in their handling (editor), because it is taken care of by overriding in owner class mechanism. Note that the class text_run is not required to override functions of its member font.
Implementing the Concept of Property
I want to interpret B::a not simply as a member of B, but as its property. In other words, when one requests the value of B::a, I want some function to get called.
Problem: There are no mechanisms in C++ allowing hiding a function behind a member's name.
Here is how we can do something like a property with standard C++:
// abstract property class class int_property { public: virtual operator int() =0; virtual int operator =(int i) =0; }; class window; // concrete property class class window_width : public int_property { public: window *owner; operator int(); int operator =(int i); }; class window { public: window_width width; }; // implementations window_width::operator int() const { return ::get_window_width(owner); } int window_width::operator =(int i) { ::set_window_width(owner, i); }
The window class exports an object of the class window_width in its interface. To begin with, there are disadvantages caused by exporting a member. Secondly, because window_width knows the interface of the owner class, the approach has the disadvantages described above in “The Problem of Dependencies”. Thirdly, the class window_width is absolutely non-reusable.
Overriding in owner class allows us make a more elegant implementation of the property concept:
// an abstract class for all integer properties class int_property { public: virtual operator int() =0; virtual int operator =(int i) =0; }; class window { public: int_property width; // a property customized for the window class width::operator int() const { return ::get_window_width(this); }; int width::operator =(int i) { ::set_window_width(this, i); } ; };