Discussion of Alternatives

From Andrey

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

Let's consider the alternatives that are used in the existing designs for C++. Suppose Helen has a pager:

class motorola2sx{ public: void call(); };
class working_girl{ public: motorola2sx pager; } Helen;

The task is to provide a mechanism that will enable working_girl class to know that the call() function of its pager member has been called. There can be the following solutions:

1. Hide the member and make its interface a subset of the interface of the owner class, not allowing the client to directly access the member:

class working_girl { public: callpager(); protected: motorola2sx pager; };

Drawbacks: it will be necessary to maintain the duplication of the pager interface by working_girl class. It will also be necessary to rewrite the telephony algorithms to make them work with the working_girl class (or its base class).

2. Create a pointer to the owner in the member.

motorola2sx::call() { owner->on_incomingcall(); }

This introduces the knowledge about working_girl into motorola2sx, which is not very logical. This in turn creates two dependencies: a dependency of classes, and a run-time dependency. We can create an abstract class for accepting pager calls with working_girl inheriting from this class, increasing the number of classes and dependencies in the system.

3. Make motorola2sx a template.

class working_girl
{ 
public: 
	motorola2sx<working_girl> pager; 
	void on_incomingcall();
};

This requires working_girl to support a certain interface. Drawbacks: the manufacturers of pagers, telephones, etc. will have to agree on a standard protocol for call notification . Any changes in the protocol will involve all the products and all people, which can result in high costs.

4. Create a special custom wrapper class working_girl_pager:

class working_girl_pager : public motorola2sx 
{ public: void call() { callback(); } };
class working_girl
{ public: working_girl_pager pager; friend class working_girl_pager; };

The working_girl_pager will have unnecessary access to the internals of motorola2sx. It will have to be adjusted every time the interface of motorola2sx changes. Working_girl_pager will have to have full access to the internals of working_girl, because it has to implement the reaction to a call. Finally, working_girl_pager is absolutely non-reusable.

5. Encapsulate the concept, which varies. Because the processing of calls is what can be different for different owners, take it outside of motorola2sx, encapsulating it in a separate class:

class pager_calls_acceptor { public: virtual void on_call()=0; };
motorola2sx::call() { calls_acceptor->on_call(); }
class working_girl_calls_acceptor : public pager_calls_acceptor { 
	working_girl* girl; 
public: 
	void on_call() { girl->callback(); } 
};

Drawbacks: creation and maintenance of the additional dynamic structures (a pager keeps a pointer to a pager_calls_acceptor, which points to a concrete working_girl_calls_acceptor keeping a pointer to working_girl which has an instance of the pager as a member).

6. Create a broadcast mechanism and send a message that Helen's pager has been called

motorola2sx::call() { broadcast->message(somebody_is _calling, this); }

The approach can incur a large computational overhead, since the message will have to be processed in all of the objects reacting to the broadcast. In addition, the broadcast code is difficult to write and debug. Finally, any object reacting to the broadcast may be able to overhear calls to Helen.

7. Inherit working_girl from motorola2sx

class working_girl : public motorola2sx { public: void call() { callback(); }};

This approach is logically unsatisfying. It distorts the purpose of working_girl, making it a subtype of motorola2sx. The internals of the base classes are exposed to working_girl, eliminating the 'black box' approach, breaking encapsulation. Besides, if there is more than one object for working_girl to control, we need multiple inheritance, possibly opening the door to method ambiguity problems.

The approaches 1-6 also have the following traits.

  • Approach 1, as the approach 7, distorts the purpose of working_girl, making it a subtype of motorola2sx.
  • Approaches 2, 3, 5, and 6, in order to adapt the member pager to work with its owner, modify the class of pager by adding the code for notifications. This leads to problems discussed earlier in “The Problem of Dependencies”, “The Problem of Notifications”, and “Code Reusability” sections.
  • The disadvantages of approach 4 are shown in “Exporting a Member Object in the Interface” section.
  • Approaches 1, 2, 4, 5, 6 add new members, functions or classes to the system.

8. Using overriding in owner class differs from most of these approaches by narrower area of application, because an object for which the overriding is done must be included in the owner class as a contained member, while most of the other approaches can be used when the object is accessible through a pointer or a reference. However, in its area of application, overriding in owner class will have certain advantages: Override the function in the owner class:

class motorola2sx{ public: virtual void call()=0; };
class working_girl
{ 
public:
	motorola2sx pager;
	void pager::call() { callback(); }
};

The results of the comparison are in Table 1. The columns represent the numbers of described solutions.

Table 1 Comparison of overriding in owner class with the alternatives

TODO: COMPARISON TABLE


Next: Questions