Applications of Overriding in Owner

From Andrey

Revision as of 06:34, 3 February 2006; view current revision
←Older revision | Newer 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:

  1. The problem of dependencies.
  2. The problem of notifications.
  3. Code reusability.
    …and to design cases:
  4. Exporting a member in the interface.
  5. Implementing the concept of a property in C++.

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 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. create_profit(&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, C1 code 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 weel1.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 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 in detail in the “Comparison with Alternatives” section later. 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); 

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 does not 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 the language 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 in “The Problem of Dependencies”. Thirdly, the class window_width is absolutely non-reusable. Overriding in owner class lets us make a more precise and concise 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); } ;
};


Next: Applying to Design Patterns