Virtual inheritance
Virtual inheritance is a form of inheritance in object-oriented programming languages.
It allows a parent to control code elements implemented in the children which inherit from it.
By using Virtual Inheritance any polymorphic functionality, within the parent, is moved into the children (functional dispersion), and then controlled (by the parent) along the lines of inheritance between parent and child (ancestor/descendent).
This is a 'language independent' definition not constrained by syntax mechanisms in specific computer languages like Eiffel, C++, C#, and Java. The term General Virtual Inheritance (GVI) should be used to qualify the language independent meaning relative to any alternate language specific meaning.
Unless qualified otherwise Virtual Inheritance (GVI) refers to the language independent meaning. Resolution Inheritance (RI) is the language independent meaning referring to state space collision resolution in the context of Multiple Implementation Inheritance (MII) of state.
Also see the Semantics section which addresses the conceptual scope of Virtual Inheritance syntax mechanisms.
General
Virtual Inheritance (VI) is a fundamental feature of most object-oriented languages. Virtual Inheritance allows a parent (the base class) to use code elements in the child (the derived class). Methods, variables and other code elements are said to be implemented in the child and controlled by the parent.
Virtual inheritance is used by a parent to specify code elements that must exist (be implemented) in the child (See Function Vs. Functionality section).
Centric to virtual inheritance is the concept of a virtual control mechanism that allows the parent to control a virtual code element in the child. The virtual control mechanism is a code element specification in the parent class. Using this mechanism the parent can then assign properties, load events or call methods even though they do not yet exist (at compile time).
A common use of Virtual Inheritance is when the parent specifies a method that must exist in the child. In this case the child will do the work (perform some function) when called upon by the parent (i.e. controlled). Parents may also specify other code elements, such as primitive data types, to exist in the child which can then be assigned and used (i.e. controlled) by the parent.
From a software design point of view this functional delegation allows polymorphic functionality in the codebase to migrate (gravitate) to child classes where it can be controlled via the lines of inheritance from the parents. The result is a functional dispersion of polymorphic code to the children.
Specification Vs. Implementation
Virtual Inheritance (VI) is a form of class inheritance and is generally contrasted with Implementation Inheritance (II).
In Virtual Inheritance an inherited child inherits only the specification of the code elements defined in the parent. As such the child is forced to implement the functionality specified by the code elements defined in the specification. This 'forcing' may lead to polymorphic behavior within the operational structure (inheritance graph) of the class hierarchy created by class inheritance.
Implementation Inheritance, on the other hand, shares an implementation of functionality located in the parent. This leads to the ability to move common code in the children to parents. The common code can then be shared by all children inheriting from that parent.
Virtual Code Elements
In Virtual Inheritance (VI) a child inherits the specification for one or more code elements. A code element may be a method, a variable or a property depending on the specific language features of an object-oriented language. Because the code element has no implementation it is called a 'virtual code element' to designate its specification only aspect. The elements are 'virtual' because they are controlled through the specification (not an implementation).
Virtual code elements (specifications via inheritance) are centric to the concept of virtual inheritance. They always force an implementation in the derived class (child class). Implementation Inheritance, on the other hand, always shares an implementation in derived classes.
It is important to note that any object-oriented inheritance phenomena can be classified under Virtual Inheritance (VI) or Implementation Inheritance (II) via this articulation. Virtual code elements never have an implementation when specified. Implementation code elements, on the other hand, always have an implementation when specified.
Use Context
Virtual code elements produced by the specification can be used only in a specific context called the 'use context'. The use context determines who can use the Virtual Control Mechanism (VCM) formed via the inherited virtual code element specification.
For example, a parent use context allows the parent to use the virtual code element. A client use context allows client objects use the virtual code element. Both parent and child use contexts may coexist depending on the features of the object-oriented language.
Other forms of use context may exist depending on the specific Virtual Inheritance (VI) features of an object-oriented language.
Code Element Binding and Coupling
Virtual code elements have bindings at the language and binary levels.
At the language level a code element may be called (method), assigned (primitive data type - single value assignment) or loaded (event - multiple value assignment). At the binary level various 'under the cover' operations are performed that connect the virtual code elements to their targets, sources and destinations. Binary bindings are processor, operating system, and compiler dependent and are not addressed at the language independent level used herein.
Code elements nominally represent class objects but they can also represent complex system level components. Virtual code elements in software patterns that represent such components are often Interface driven. The connections that couple these pattern participants may be 'tight or loose' depending on the intentions of the designer.
Other issues of connecting code elements effect the determination to use tight or loose coupling. These kinds of concerns are often 'under the cover issues' at the binary level which effect choices at the language level. For example;
- Is the code element in the same process?
- Is the code element in the same thread?
- Is the code element on the same CPU?
- Is the code element on the same machine?
Tight and loose coupling can determine parent or client use context for virtual code elements specified in virtual inheritance.
Thus the two aspects of binding (call, assignment, load, etc) and coupling (tight/loose) have important implications on the use context of the virtual code element. Therefore virtual code element binding and coupling, at the language level, are fundamental aspects of Virtual Inheritance (VI).
Abstract Method
An abstract method is one defined by the parent with no default implementation. Abstract methods are a definitive expression of Virtual Inheritance as they embody the forced implementation in the child by the specification in the parent (a primary requirement for delegation of functionality via inheritance).
Abstract methods are signature only specification which leave the implementation to the child. The signature specification (of the abstract method) is the virtual control mechanism that allows the parent to call the child without knowing the details of the child's implementation of the parent's specification. Thus the signature produces a virtual code element usable by the parent.
Note the distinction between an 'abstract method' (which can never provide a default implementation) and a 'default virtual method' which will always provide a default implementation. The abstract method is a Virtual Inheritance (VI) phenomena while the default virtual method is an implementation Inheritance (II) phenomena.
Note that abstract methods are just one of many kinds of code elements that can be specified by a parent. Assignable code elements such as events or primitive data types in interfaces distinguish Virtual Inheritance (VI) from Virtual Functions (which are conceptual subset of VI). Code element binding is why parents (GVI) and clients (SVI) are said to 'control' virtual code elements rather than just call them (as with abstract methods).
Interfaces
Virtual Inheritance is the core impetus for Interfaces. With Interfaces a child inherits only the specification and not the implementation of a set of code elements. Thus Interfaces force a child to implement code elements defined in the interface.
An interface is a Virtual Control Mechanism (VCM) that contains a set of virtual code elements. Nominally the use context of the interface is applicable to each of its virtual code elements.
Note that Interfaces (language independent meaning) are more than just method specifications as they can include all types of code elements (state space code elements as well as operational elements). This is in keeping with the 'general' and 'inclusive' nature of General Virtual Inheritance (GVI).
Interfaces provide a formal specification mechanism that allows a virtual code element within the interface to be controlled by the parent.
Type Polymorphism
In Type Polymorphism the child may use both its implementation (defined in the child code body) and the parent's implementation (of the virtual method) in implementing the child's version of the target functionality.
The 'abstract method' and 'forced implementation' are what distinguish Virtual Inheritance (GVI) from the more general 'Type Polymorphism'.
Thus Type Polymorphism (TP) is a conceptual superset of Virtual Inheritance (GVI) in that TP functionality can be composed of child AND parent implementations while GVI functionality is (by definition) the child implementation alone.
Control Mechanism Vs. Polymorphic Mechanism
Virtual Inheritance (VI) has both a control and polymorphic aspect to its conceptual semantics.
Control of a virtual code element by a parent or client is centric to its use context. In this section the control aspect is articulated in terms of the parent use context. Thus, it is important to note that this section is equally applicable to the client use context of a virtual code element. The key point of a 'control mechanism' is that its use is centric to its 'use context' (parent, client, etc).
The virtual control aspect is the ability of a parent to control a 'virtual code element' that is ultimately implemented in the child. The word 'control' means the code element may be callable (an abstract method), assignable (a primitive data type) or both (an event). Whatever the relationship (callable, assignable, etc) the parent 'controls' a 'virtual' code element. Thus the semantic control mechanism (of virtual inheritance) is centric to the parent.
The polymorphic aspect is the ability of the child implementation to type the functionality (of its virtual implementation) to the child's specific needs (for that functionality). Thus the semantic polymorphic mechanism is centric to the child.
In articulating these two aspects the relationship of Virtual Inheritance to Type Polymorphism becomes clear. The 'virtual control' functionality of 'Virtual Inheritance' is the primary benefit to the parent class. The polymorphic 'potential' of Virtual Inheritance is the primary benefit to the child class. Note that polymorphism (in the child) is NOT a requirement of General Virtual Inheritance (GVI).
The semantic effect of 'virtual control' is that the parent can delegate responsibility for the functionality to the child. This functional delegation, in turn, leads to the polymorphic aspect of Virtual Inheritance via the actual implementation in the child.
Note that the virtual code element in the child is virtual from the parent's point of view and actual from the child's point of view.
Thus Virtual Inheritance is a Virtual Control Mechanism (VCM) for parents that allows functional delegation to the child. If a single line of inheritance exists (between first ancestor and final descendent) with several layers of inheritance then no polymorphism exists (its just pure functional delegation).
Virtual Control Mechanism (VCM)
The difference between a Virtual Control Mechanism (VCM) and a 'virtual code element' is best seen in Interfaces. A VCM can be a single mechanism that contains a set of virtual code elements. On the other hand a VCM can also contain (or represent) a single virtual code element such as an abstract method. Interfaces are a VCM that can contain multiple virtual code elements.
Nominally a VCM has a use context that is applicable to all the virtual code elements it contains.
A VCM can also include metadata not seen in the virtual code element. The metadata controls the use of the virtual code element.
The VCM metadata aspect of Virtual Inheritance (VI) is seen in the signature of abstract methods. The abstract method signature specification is the Virtual Control Mechanism that allows the parent to call the abstract method without knowing the details of the method's implementation of the parent's specification. The virtual code element (in this example) would be the abstract method and its metadata determines the context of its use (use context).
Different Forms of Virtual Inheritance
Virtual Inheritance (VI) is a phenomena of specification that operates along the lines of inheritance between parent and child. There are several fundamental aspects of Virtual Inheritance (VI) which are;
- Lines of inheritance
- Virtual code elements
- Use context
- Code Element Binding (call, assignment, load, etc)
- Code Element Coupling (tight, loose, etc)
The combination and permutation of these fundamental aspects create the different forms of VI. Several forms of VI, used herein, are listed below.
- GVI - General Virtual Inheritance - Parent use context
- SVI - Simple Virtual Inheritance - Client use context
GVI and SVI compliment one another and may or may not coexist in the same virtual code element depending on the VI features of the object oriented language.
Note that the terminology for the different forms of VI in the software industry will evolve along with the advancement of virtual inheritance features in newer object-oriented languages.
The most common forms of VI are covered in this article. Note that other forms may exist or be developed in the future.
Use context, lines of inheritance and code element binding/coupling (class/component) are important foundation concepts in understanding the role and form Virtual Inheritance (VI) takes in the object oriented design process.
General Virtual Inheritance (GVI)
General Virtual Inheritance (GVI) has two primary criterion for its architecture;
- (1) Virtual Control Mechanism (VCM)
- (2) Direct Parental Use of the VCM (parental use context)
The first GVI criterion is that the Virtual Control Mechanism is an abstract specification mechanism that can then be used to control the target code element (as a 'virtual code element'). The specification is virtually inherited and will force the implementation to exist in the derived class.
The second GVI criterion is that the direct parental use context allows the 'parent' to 'use' the code elements (VCM - in the child specification) as if the implementation (of the code elements) were its own.
Simple Virtual Inheritance (SVI)
Simple Virtual Inheritance has two primary criterion for its architecture;
- (1) Virtual Control Mechanism (VCM)
- (2) Client Use of the VCM (client use context)
The first SVI criterion (just like GVI) is that the Virtual Control Mechanism (i.e. the virtual code element) is an abstract specification mechanism that can then be used to control the target code element (the implementation code element). The specification is virtually inherited (no implementation passing into the child via inheritance) and will force the implementation to exist in the derived class.
The second SVI criterion is that the direct client object use context allows a 'client' to 'use' the code elements (VCM - in the child specification) as an accessible feature of an instance of the child.
Client use context requires a child object be instantiated. Once instantiated then a 'client' (some other object (class) or expression (main)) can use the VCM as if an implementation existed. Use of the implementation code element is always via the virtual code element.
Languages
C++
Virtual Inheritance is a term that has historically been used in three major Object-Oriented Language (OOL) contexts.
- The C++ community,
- The non C++ community
- Language independent OOL phenomena (general conceptual semantics in OOLs)
Although there are other OOL contexts (for 'Virtual Inheritance') the C++ sections below seek to explain Virtual Inheritance phenomena within C++ in a language independent context (conceptual semantic meaning in a more general context). The intention is to clearly articulate the conceptual semantics of the phenomena and show examples in specific languages such as C++.
It is necessary, therefore, to explain the difference between Virtual Inheritance (VI)
and the C++ 'virtual
' keyword.
C++ virtual Keyword Vs. Virtual Inheritance (VI)
Virtual code elements are centric to the concept of Virtual Inheritance (VI). Implementation code elements, on the other hand, are centric to Implementation Inheritance (II) .
Note that
the C++ 'virtual
' keyword
does not connote
the phenomena involving
virtual code elements.
In C++ the 'virtual
' keyword is used to resolve ambiguity problems.
Ambiguity problems, caused by collisions between implementation code elements, are Implementation Inheritance (II) phenomena. The resolution of these collisions include Method Override Ambiguity (MOA) and Inherited Class Ambiguity (ICA).
Historically the term 'virtual inheritance' in C++ deals with Implementation Inheritance (II) phenomena (as described above). In the context of this article, however, the term Virtual Inheritance (VI) refers to all inheritance phenomena related to virtual code elements (as defined above).
In the C++ programming community the term 'virtual inheritance' is the historic
C++ Diamond problem
resolution meaning originating from the C++ virtual
keyword used in inheritance.
To avoid confusion this article uses the term Resolution Inheritance (RI) to qualify syntax mechanisms
that resolve potential conflicts due to the collision of implementation code elements (such as the diamond problem).
Note the
Stateful Diamond Point pattern
is the basis of the
example code (below)
that shows C++ inheritance in a language independent context.
In contrast to the resolution of ambiguity problems, Virtual Inheritance (VI) deals with specifications. General Virtual Inheritance (GVI) in C++ is similar to other languages (such as Java and C#) in that a specification can be defined in a parent and inherited by a child. In C++ Virtual Inheritance (GVI) is demonstrated in the virtual code elements defined in abstract methods.
C++ Abstract methods
C++ uses the virtual
keyword to define abstract methods.
Abstract methods are virtual code elements defined by a parent and
implemented in a child.
With C++ abstract methods a parent can define the code element specification and use that specification within the body of its methods. The same virtual code element (abstract method) defined by the parent is also available to clients. The use context of abstract methods allows both parent and client to use the same code element specification.
In C++ the use context of abstract methods not restricted in any way. This compares with virtual code elements defined in C# Interfaces which have a client only use context.
The Humanoid example code below shows virtual code elements via abstract methods. The example shows that the use context of abstract methods in C++ is less restricted than the use context of virtual code elements defined in C# interfaces.
// Humanoid Example // class Humanoid { protected: bool Impersonation_flag; virtual void Eye_color() = 0; // Use_GVI :_: Abstract method - Parent use context public: virtual void Be_honest() = 0; // Use_SVI :_: Abstract method - Client use context void Print_eye_color() { Eye_color(); // Show_GVI :_: Use virtual code element defined above } };
class Human : public Humanoid { virtual void Eye_color() { printf("Eye color is : Green\n"); } void Be_honest() { /* Human is always honest */ } };
class Mutant : public Humanoid { public: Mutant() { Impersonation_flag = true; } void Be_honest() { Impersonation_flag = false; } virtual void Eye_color() { if ( Impersonation_flag ) { printf("Eye color is : Green\n"); } else { printf("Eye color is : Red\n"); } } };
void main() { Human human; Mutant mutant; // Humanoid * human_impersonator_ptr; // human_impersonator_ptr = & mutant; // mutant. Print_eye_color(); human. Print_eye_color(); // human_impersonator_ptr -> Print_eye_color(); // Show_SVI :_: Client calls method implementation forced via parent specification human_impersonator_ptr -> Be_honest(); human_impersonator_ptr -> Print_eye_color(); }
// Test driver (main) output // // Eye color is : Green // Eye color is : Green // Eye color is : Green // Eye color is : Red //
Abstract Method :
Abstract methods in C++ are virtual code elements that exhibit both GVI and SVI
phenomena. When the parent defines an abstract method there is no requirement
to use the abstract method in the body of its code (SVI only access).
In either case (parental GVI or client only SVI access) the child is
forced to implement the parents code element specification (the abstract method).
Use Context : In C++ Abstract methods are accessible to both parent and client. This is similar to C# abstract methods which also allow a parent/client use context. This is in contrast to virtual code elements defined in C# Interfaces which are restricted to a client only use context.
Virtual Code Elements : Use context restrictions in Virtual Inheritance (VI) have important implications relative to single inheritance and multiple inheritance of virtual code elements.
As the C++ and C# use context of abstract methods shows the multiple inheritance of C# interfaces is NOT COMPARABLE with multiple inheritance of abstract methods in C++ because of use context restrictions (GVI Vs. SVI).
Resolution Inheritance
Resolution Inheritance (RI) is an Implementation Inheritance (II) phenomena. It is important to distinguish Virtual Inheritance (VI) phenomena from Resolution Inheritance to show that the two concepts are actually separate things.
In the C++ programming community the term 'virtual inheritance' is the historic C++ Diamond problem resolution meaning.
In the general context (language independent) of other languages that support
Multiple Implementation Inheritance (MII) a better term (than the historic C++ 'virtual inheritance' meaning) would be
Resolution Inheritance (RI). In a hypothetical object-oriented language, for
example, the use of a resolve
keyword, instead of the C++
virtual
keyword, would be more accurate to the conceptual
semantics of state space collision resolution (see below). The conceptual
semantic of resolution is the key point to keep in mind in the context of
Multiple Implementation Inheritance (MII)
of implementation code elements.
With this in mind it is especially important to show both RI and VI in the same code example to demonstrate the difference between virtual code elements (VI) and implementation code elements (II).
The ability to show the historic C++ Diamond problem in the context of Virtual Inheritance (as defined herein) helps to articulate Resolution Inheritance as an Implementation Inheritance (II) phenomena. This, in turn, helps to distinguish Virtual Inheritance phenomena in C++ and other languages. Finally the combined examples (in C++ and other languages) give an accurate presentation of the coexistence and complimentary nature of VI and II in software design.
Stateful Diamond Point (SDP) Pattern in C++
A short but effective example of Virtual Inheritance in the context of other inheritance phenomena is presented below (High State Tracker). It shows how Virtual Inheritance compliments other inheritance phenomena (such as Implementation Inheritance). It also shows that Virtual Inheritance (GVI in all object-oriented languages) coexists with Resolution Inheritance (RI in C++).
This example uses a specific pattern called the
Stateful Diamond Point (SDP) pattern
to clearly articulate multiple inheritance of state without polymorphism. The
class participants are listed below.
Pattern Role | Example Role | Class | Implementation Description |
Foundation | State machine | Base
|
Controls current system state |
Functional Supporter | Global manager | Internet_child
|
Logs high states to global office |
Functional Supporter | Local manager | Database_child
|
Logs high states to local home office |
Execution Point | Session manager | Diamond_tracker
|
Tracks high states in the current session |
The SDP pattern features a descendent (Execution Point) that is controlled by the base (Foundation) and uses a functionally orthogonal middle layer (supporters) to execute the functionality of a Virtual Method implemented in the descendent. In the inheritance tree the state machine forms the foundation, followed by functionally orthogonal support managers, which, are joined at the 'Execution point'.
The SDP pattern name is derived from the emphasis on stateful MII (Multiple Implementation Inheritance of state). The Stateful is contrasted to other similar Diamond Point patterns that would emphasize polymorphism or a combination of common state space (stateful MII) and polymorphism.
The SDP pattern name also emphasizes the Diamond Point via the design concern to deal with the surface API of the final descendent. The Execution Point class implements the Virtual Method. The Point of the design is the desired functionality (Surface API) available to any client classes that use the pattern to perform some target functionality.
In the SDP pattern the core design concern is the functionality (the Point of the design)
presented by the surface API at the Execution Point. This exposed functionality
(surface API) helps to contrast Object Aggregation and Composition design approaches
from Inheritance approaches (balanced GVI/MII for polymorphism and normalization).
Note that, in this pattern, the functional supporters should be functionally orthogonal.
The SDP pattern has a characteristic 'Arrow' data flow pattern embedded within it.
Note that the data flows;
- From the (1) execution point to the (2) foundation
- From the (2) foundation to the (3) execution point
- From the (3) execution point to the (4,5) support classes
Although not shown in the example, the forward flow of data can be
(A) conditional and (B) expansive. Conditional data
flow is dependent on if
statements at points 1,2,3 and 4.
Expansive is additional data, such as in data structures and messages,
that is added to the original incoming datum at each point (1,2,3,4).
The High State Tracker example of the SDP pattern is more reflective of real
world production software (then the shorter C++ examples herein). It features
the following C++ Virtual Inheritance phenomena (GVI/RI).
Code Feature | Implementation Description |
Coexistence | GVI, RI, MII of state space, SII of state space |
GVI Control mechanism | Virtual method specification - Base::Log_state()
|
GVI Implementation | Virtual method implementation - Diamond_tracker::Log_state()
|
RI Resolution mechanism | Inheritance in support layer - virtual Base
|
MII of state | Base::Current_state
|
SII of state | Highest_local_state , Highest_global_state
|
No polymorphic phenomena | Only one instance of Log_state() in one child (Diamond_tracker )
|
No polymorphic children | Diamond_tracker has no peer children in inheritance tree
|
Thus the High State Tracker example is reflective of a simple diamond problem (ICA)
that exhibits Multiple Implementation of state Inheritance (MII) but without
any polymorphism.
Coexistence of Virtual and Resolution Inheritance in C++
The coexistence of virtual (GVI) and resolution (RI) inheritance in C++ is demonstrated in the High State Tracker example code presented below.
Note that there is no polymorphism phenomena related to GVI in this code example
The parent (Base) only controls (control phenomena) the child (Diamond_tracker). There is no 'multiple versions of children' (peers to Diamond_tracker in the inheritance tree) with multiple versions of a virtual method (see Log_state() below).
// High_State_Tracker Example // //#------------------------------------------------------------------------------------------------# //# State machine :_: Manages state space - Ties all concerns together //# Use GVI - Specify Child 'Log_State()' and use it as Virtual Method //#------------------------------------------------------------------------------------------------# class Base { private: int Current_state; // Initialize to zero (not shown in example) public: Base() { Current_state = 0; } // void Set_state(int next_state) { Current_state = next_state; // Move system to the new state (primary state transition) printf(" : Current state is - %d \n",Current_state ); Log_state( Current_state ); // Use GVI : Control children and keep history of state changes } // int Get_current_state() { return Current_state; } // virtual // Virtual Method (Shows GVI - General Virtual Inheritance) void Log_state(int new_state){}; // Specify here : Instantiate in Diamond class }; //#------------------------------------------------------------------------------------------------# //# Global Manager :_: Logs high states to global office - manages global concerns //# Use RI - Virtual forces one copy of Base in Diamond_tracker //#------------------------------------------------------------------------------------------------# class Internet_child : virtual public Base { private: int Highest_global_state; // Could be set to highest Home Office State (not shown in example) void Log_to_home_office(int latest_high_value) { // Code to send value to home office via Internet would go here } protected: void Log_global_high_value(int new_state) { if ( new_state > Highest_global_state ) { Highest_global_state = new_state; // Save the 'next highest state' Log_to_home_office( new_state ); // Inform home office } } public: Internet_child() // Internet retrieval could go here { Highest_global_state = 70; // Init for demo : Highest 'global' state } // int Get_global_highest_state() { return Highest_global_state; } }; //#------------------------------------------------------------------------------------------------# //# Local Manager :_: Logs high states to local home office - manages local concerns //# Use RI - Virtual forces one copy of Base in Diamond_tracker //#------------------------------------------------------------------------------------------------# class Database_child : virtual public Base { private: int Highest_local_state; // Could be set to highest local Office State (not shown in example) void Log_to_local_database(int latest_high_value) { // Code to save value in local database would go here } protected: void Log_local_high_value(int new_state) { if ( new_state > Highest_local_state ) { Highest_local_state = new_state; // Save the 'next highest state' Log_to_local_database( new_state ); // Save to local database } } public: Database_child() // Database retrieval could go here { Highest_local_state = 30; // Init for demo : Highest 'local' state } // int Get_local_highest_state() { return Highest_local_state; } }; //#------------------------------------------------------------------------------------------------# //# Session Manager :_: Tracks high states in the current session - manages immediate concerns //# Use GVI - Parent (Base) controls child (Diamond_tracker::Log_state()) //# Use RI - One copy of Base, //#------------------------------------------------------------------------------------------------# class Diamond_tracker : public Internet_child, public Database_child { private: int Session_state; // Save las int Highest_session_state; // void Log_state( int new_value ) // Use GVI : Parent will control child implementation { Log_local_high_value ( new_value ); // Save if highest locally Log_global_high_value ( new_value ); // Save if highest globally } public: Diamond_tracker() { Session_state = 0; Highest_session_state = 0; } // void New_state(int new_value) { Session_state = new_value; // Move session manager to new state if ( Session_state > Highest_session_state ) { Highest_session_state = new_value; // Track high states for this instance } Set_state( new_value ); // Move system to new state via Base } // int Get_session_highest_state() { return Highest_session_state; } }; //#------------------------------------------------------------------------------------------------# //# Test Driver :_: Drives the high state tracker (Diamond_Tracker) //# Show RI and GVI coexisting and working together //#------------------------------------------------------------------------------------------------# void main() { Diamond_tracker my_diamond; // printf("\n"); printf(" Start : Highest session state is : %d \n", my_diamond.Get_session_highest_state() ); printf(" Start : Highest local state is : %d \n", my_diamond.Get_local_highest_state() ); printf(" Start : Highest global state is : %d \n", my_diamond.Get_global_highest_state() ); printf("\n"); // my_diamond.New_state( 10 ); my_diamond.New_state( 30 ); // Highest local state at start my_diamond.New_state( 40 ); // printf("\n"); printf(" Middle : Highest session state is : %d \n", my_diamond.Get_session_highest_state() ); printf(" Middle : Highest local state is : %d \n", my_diamond.Get_local_highest_state() ); printf(" Middle : Highest global state is : %d \n", my_diamond.Get_global_highest_state() ); printf("\n"); // my_diamond.New_state( 70 ); // Highest global state at start my_diamond.New_state( 10 ); my_diamond.New_state( 80 ); // printf("\n"); printf(" End : Highest session state is : %d \n", my_diamond.Get_session_highest_state() ); printf(" End : Highest local state is : %d \n", my_diamond.Get_local_highest_state() ); printf(" End : Highest global state is : %d \n", my_diamond.Get_global_highest_state() ); printf("\n"); } //#------------------------------------------------------------------------------------------------# //# Output :_: Show the state transitions - Highest state logged locally and globally //# Show RI produces one state space copy ( Current_State ) //# Show GVI allows parent to control child ( Log_state() ) //#------------------------------------------------------------------------------------------------# // // Start : Highest session state is : 0 // Start : Highest local state is : 30 // Start : Highest global state is : 70 // // : Current state is - 10 // : Current state is - 30 // : Current state is - 40 // // Middle : Highest session state is : 40 // Middle : Highest local state is : 40 // Middle : Highest global state is : 70 // // : Current state is - 70 // : Current state is - 10 // : Current state is - 80 // // End : Highest session state is : 80 // End : Highest local state is : 80 // End : Highest global state is : 80 //
Note the Internet and database code are not present but noted via comment
(Log_to_home_office(), Log_to_local_database()
). Also note that
the key initial states (Highest_local_state/Highest_global_state
)
are not initialized from a database or the Internet. This is to keep the
example as short as possible and still present features and phenomena relevant
to GVI/RI.
In the code the test driver (Main()
) calls the session manager
(my_diamond
) to track the highest state in a state transition
sequence. Transitions are printed to the console. The starting, middle and
ending state space is printed as well.
The focus of the design is to show how the parent (Base) controls the
child (
my_diamond) using GVI (
log_State()
). It shows that
GVI coexists and operates with RI (virtual Base inheritance
). It
shows that RI resolves ambiguity (ICA) in C++. Each state transition also
shows Multiple Inheritance of state Implementation (MII of state -
Current_state
) which is the primary basis for RI (which instance
of state - state space collision) existence.
Summary of Virtual and Resolution Inheritance in Object-oriented Languages
Inheritance in C++ is seemly complex in that many semantic aspects
(control/polymorphism/resolution/etc) are in effect at the same time (as in the High
State Tracker code example). It is therefore necessary for the terminology of
object-oriented (OO) computing to articulate the phenomena of Virtual
Inheritance precisely.
By articulating the difference between virtual (GVI) and resoultion (RI) inheritance we can discuss the role of
polymorphism in C++ (without reference to other OO languages) as well as compare
inheritance phenomena in C++ to other object-oriented languages (MII Vs. SII).
Ambiguity (ICA) in state space collision (MII) MAY have polymorphic resolution aspects but it
(polymorphism) is not REQUIRED. In 'High State Tracker' no polymorphism is
selected or resolved. This example shows only a 'collision of state space' (via
the potential for Base class multiple instances) being resolved (via RI). No
resolution of virtual methods (as in the Human/Mutant example) is present
Note that the functional similarity of logging to a database or the Internet is not
polymorphism in the strict sense (dispatch VTables, etc). From a system point of
view database and Internet logging are somewhat polymorphic (conceptually similar via
persistence). From a syntax mechanism point of view, however, they have different
functionality (Internet messaging Vs. database access).
High State Tracker also shows that POLYMORPHISM (different children with different
behavior) is a separate phenomena from GVI (control) and RI (resolution). Polymorphism is child
centric. General Virtual Inheritance (GVI) and Multiple Implementation Inheritance (MII)
both allow an 'opportunity' for polymorphism to exist (i.e. they do not REQUIRE
it in children). Thus, in the case of functionally orthogonal and functionally normalized
code, polymorphic phenomena need not be present (as this example shows via Log_state() and Current_state).
The example code also shows that you can have GVI in a control only 'mode' (no
polymorphism where only the control aspect phenomena exists - Log_state()). This emphasizes
the fact that Type Polymorphism (polymorphism) IS NOT General Virtual
Inheritance (control). The example also proves that GVI has control centric
semantics as its primary phenomena (Log_state()).
From a language independent perspective RI is actually a Multiple
Implementation Inheritance (MII) phenomena of resolution. As such
the term 'Resolution Inheritance' (RI) is a more accurate rendition of
the conceputal semantics needed to resolve state space collisions.
State space collision (which instance of state) and its resolution is NOT a
problem. Current Object-Oriented Languages (OOLs) have had controversy
handling this issue. That does NOT imply that future OOL syntax will NOT correctly
permute and solve the conceptual semantics of MII state space implementation
(instance generation) options.
C#
The code element specification (in all forms of virtual inheritance - GVI, Interfaces) becomes a
Virtual Control Mechanism (VCM) in itself.
This means that all virtual code elements (specified via inheritance) have two important aspects to their design.
- (1) Specification : The specification can be used independently of how it is implemented.
- (2) Use context : The specification can be used only in a specific context (the use context)
Since C# does not have Multiple Implementation Inheritance (MII) it can not have multiple sources (parents)
of virtual code elements (via GVI). C# offers Single Implementation Inheritance (SII) with Multiple Interface Inheritance (SVI - see below).
This creates significant differences in the use context when compared to other languages.
It is important to understand the difference in the use context of virtual code elements (GVI Vs. SVI).
The use context determines who can use the virtual control mechanism formed via the
inherited virtual code element specification. The 'use context' determines if the parents can directly
use the virtual code elements or not.
The sections below highlight the core aspects of virtual inheritance as it exists in the C# language.
Simple Virtual Inheritance (SVI) via Interface Inheritance
The advantage with
Interfaces
is that the Virtual Control Mechanism (VCM) has its own type.
Another advantage is that the 'type' is a set of code elements. A disadvantage of interfaces
is that the virtual code elements ARE NOT usable by the parent (as they are in GVI).
Interfaces allow for a 'Simple Virtual Inheritance' (SVI not GVI) of the specification
that does not allow direct parental use of the virtual control mechanism. Interface
usage is open to all clients of the interface as a new object type. In effect the Interface
is a contract specifcation that separates what an object (interface instance) does from
how it does it.
Both GVI and interfaces (SVI) use 'virtual inheritance' to specify a control mechanism for code elements
(specifications via inheritance).
They differ however in WHO is going to use the Virtual Control Mechanism. With GVI its the parents who use
the code elements.
With Interfaces its everyone else who uses the code elements (which MAY be a 'parent-as-client').
GVI provides parents a 'virtual' code element.
Interfaces (SVI) provide client objects a 'virtual' set of code elements (contained in the interface implementation).
Use Context of Virtual Code Elements in C#
The Subject Interface example below shows Virtual Inheritance (VI) in the context of
Interface Inheritance.
It demonstrates the
Virtual Control Mechanism (VCM)
aspect of Interfaces (SVI).
It shows that virtual control mechanisms can contain
assignable state space (in addition to callable code elements like methods).
Finally it shows the
client use context
of the VCM specified by the Interface.
using System;
//
// Subject Interface Example
//
//#------------------------------------------------------------------------------------------------#
//# Binding Mechanism :_: Delegate for event binding between subject and observer
//# Use_SVI(4) an Interface event shows a loadable virtual control element
//#------------------------------------------------------------------------------------------------#
public delegate void Delegate_signature( string p_notification_message );
//#------------------------------------------------------------------------------------------------#
//# Pattern Manager :_: Binds the observer to the subject and fires subject events
//#------------------------------------------------------------------------------------------------#
class Observer_manager
{
private Subject_API_surface m_subject_ref;
//
//#------------------------------------------------------------------------------------------------#
//# Subject Interface :_: Subject interface used by binder to load delegates and fire events
//# Use_SVI(1) to define VCM as set of virtual code elements in specification
//# Use_SVI(4) an Interface bool shows an assignable virtual control element
//#------------------------------------------------------------------------------------------------#
public interface Subject_API_surface
{
bool Fire_event(string signal_value); // Method : Will fire events in subject
event Delegate_signature notification_targets; // Event : Subscription list of observers
bool Notification_gate_flag // Property : Turns notification (via event) on/off
{
get;
set;
}
}
//#------------------------------------------------------------------------------------------------#
//# Subject Binder :_: Binds the manager to a preexistent subject defined outside the manager
//# Use object composition for external pattern participants
//#------------------------------------------------------------------------------------------------#
//
public Observer_manager( Subject_API_surface p_subject )
{
m_subject_ref = p_subject;
}
//
//#------------------------------------------------------------------------------------------------#
//# Pattern Driver :_: Creates pattern participants, binds participants, fires subject events
//# Use object aggregation for internal pattern participants
//# Use_SVI(2) to drive different subjects via the Contract aspect of VCM
//#------------------------------------------------------------------------------------------------#
public
void Main_process()
{
Observer target_1 = new Observer("Number One");
Observer target_2 = new Observer("Number Two");
//
m_subject_ref.notification_targets += target_1.Notify_me; // Loadable virtual code element
m_subject_ref.notification_targets += target_2.Notify_me;
//
m_subject_ref.Notification_gate_flag = true; // Assignable virtual code element
m_subject_ref.Fire_event( "Event_1" );
m_subject_ref.Notification_gate_flag = false;
m_subject_ref.Fire_event( "Event_2" );
m_subject_ref.Notification_gate_flag = true;
m_subject_ref.Fire_event( "Event_3" );
}
}
//#------------------------------------------------------------------------------------------------#
//# Subject_A :_: Contains events that notify any observers (notification targets)
//# Use_SVI(3) do not have a notification count for this subject type
//#------------------------------------------------------------------------------------------------#
class Subject_A : Observer_manager.Subject_API_surface
{
private string m_my_identity;
string m_notification_message;
//
public Subject_A(string p_my_identity )
{
m_my_identity = p_my_identity;
}
bool notification_gate_flag;
//
public bool Fire_event( string p_event_identity )
{
if ( Notification_gate_flag == false ) return false;
//
m_notification_message = "Notification : "
+ m_my_identity
+ " : "
+ p_event_identity;
//
notification_targets( m_notification_message );
return true;
}
//
public event Delegate_signature notification_targets;
//
public bool Notification_gate_flag
{
get
{
return notification_gate_flag;
}
set
{
notification_gate_flag = value;
}
}
}
//#------------------------------------------------------------------------------------------------#
//# Subject_B :_: Contains events that notify any observers (notification targets)
//# Use_SVI(3) have a notification count for this subject type
//#------------------------------------------------------------------------------------------------#
class Subject_B : Observer_manager.Subject_API_surface
{
private string m_my_identity;
string m_notification_message;
int m_notification_count;
//
public Subject_B(string p_my_identity )
{
m_my_identity = p_my_identity;
}
bool notification_gate_flag;
//
public bool Fire_event( string p_event_identity )
{
if ( Notification_gate_flag == false ) return false;
//
m_notification_count ++;
m_notification_message = "Notification "
+ m_notification_count.ToString()
+ " : "
+ m_my_identity
+ " : "
+ p_event_identity;
//
notification_targets( m_notification_message );
return true;
}
//
public event Delegate_signature notification_targets;
//
public bool Notification_gate_flag
{
get
{
return notification_gate_flag;
}
set
{
notification_gate_flag = value;
}
}
}
//#------------------------------------------------------------------------------------------------#
//# Observer :_: A 'notification target' - waits for notification - prints notification
//#------------------------------------------------------------------------------------------------#
public class Observer
{
string my_identity;
//
public Observer( string my_new_identity )
{
my_identity = my_new_identity;
}
//
public void Notify_me(string p_notification_message )
{
Console.WriteLine(" Observer Instance {0} : Notification message - {1} ",
my_identity,
p_notification_message
);
}
} // End_of_class
//#------------------------------------------------------------------------------------------------#
//# Test Driver :_: Main entry point for console application - Creates, runs binder/manager
//#------------------------------------------------------------------------------------------------#
class Test_driver
{
static void Main ()
{
Subject_A subject_A = new Subject_A( "System A");
Subject_B subject_B = new Subject_B( "System B");
//
// Bind subjects to the pattern
//
Observer_manager manager_A = new Observer_manager ( subject_A );
Observer_manager manager_B = new Observer_manager ( subject_B );
//
// Bind observers to the pattern and drive system
//
manager_A. Main_process();
manager_B. Main_process();
}
}
//#------------------------------------------------------------------------------------------------#
//# Output :_: Show pattern binding via interface contract specification
//#
//# Conceptual aspect 1 Show_SVI(1) as a 'client use context' VCM (manager VCM for subject)
//# Conceptual aspect 2 Show_SVI(2) contract aspect, driving different type objects (Subject A,B)
//# Conceptual aspect 3 Show_SVI(3) contract does not specify behaviour (polymorphic output A,B)
//# Conceptual aspect 4 Show_SVI(4) virtual code elements are 'controllable'
//#------------------------------------------------------------------------------------------------#
//
// Observer Instance Number One : Notification message - Notification : System A : Event_1
// Observer Instance Number Two : Notification message - Notification : System A : Event_1
// Observer Instance Number One : Notification message - Notification : System A : Event_3
// Observer Instance Number Two : Notification message - Notification : System A : Event_3
//
// Observer Instance Number One : Notification message - Notification 1 : System B : Event_1
// Observer Instance Number Two : Notification message - Notification 1 : System B : Event_1
// Observer Instance Number One : Notification message - Notification 2 : System B : Event_3
// Observer Instance Number Two : Notification message - Notification 2 : System B : Event_3
//
Virtual Control Mechanism (VCM) :
The Subject_API_surface
interface defines the set of virtual code elements
needed by the manager to control the subject. The specification, via the Interface,
forces the child to implement the code elements it defines. The elements are
'virtual' because they are controlled through the specification (not an implementation)
defined by the interface.
The VCM allows different types of objects to be controlled by the same set of virtual code elements.
Any class that inherits the Interface will be forced to provide the set of 'implementation
code elements' represented by the virtual code elements.
The Observer_Manager
class displays this capability
by being able to control the Subject_A
and Subject_B
object types (classes).
Assignable State Space :
In the example above the Main_process
of the Observer_manager
contains
the Notification_gate_flag
code element which is an assignable code element (single value assignment).
The notification_targets
code element is a loadable code element (multi-value assignment).
In Virtual Inheritance code elements are said to be 'controllable'. This means
that a code element can be called (method), assigned (bool) or loaded (event).
The conceptual semantics of the 'control aspect' do not, and should not, address
the implementation details of a particular compiler or language.
This control aspect is an important
difference between virtual inheritance and virtual functions (VTables - compiler implementation).
Client Use Context :
Note that virtual code elements, established via inheritance (in C#, C++, java or
any language), ALWAYS have a use context aspect. The use context aspect determines how
the specification can be leveraged in the software design architecture.
For example, can the parents leverage the code elements for polymorphic control
(GVI)? Can client objects leverage the 'interface aspect' and drive a target
object via the 'interface' without a concern about other 'aspects' of the object
(SVI - contract aspect as in the code above)?
The Subject Interface code shows that Simple Virtual Inheritance (SVI)
has a client use context aspect to its conceptual semantics.
In GVI the parents have control.
In SVI everyone, except the parents, has control.
Object-oriented languages may feature other use contexts for virtual
code elements besides parent/client (GVI/SVI). Thus it is important to
note that Virtual Inheritance (VI) is centric first to the specification
aspect (VI) and then to the use context aspect (GVI/SVI/etc).
Stateful Diamond Point (SDP) Pattern in C#
Understanding the use context of virtual code elements is important in understanding
the design advantages and disadvantages of virtual inheritance. The Stateful
Diamond Point pattern (presented in the C++ section) is implemented in the
C# language below. It shows the coexistence of General and Simple virtual inheritance (GVI and SVI).
In C# the same pattern participants exist
but are connected using Single Implementation Inheritance (SII) and object aggregation.
Virtual inheritance (VI), in the context of the C# design approach (GVI/SII), can be
compared to VI in the C++ design approach using functional normalization,
aggregation and dispersion along the lines of inheritance (GVI/MII).
The comparison shows how VI (GVI/SVI) effects the internal functionality of the SPD pattern.
In the C# SPD pattern implementation the core design concern is still
the functionality (the Point of the design)
presented by the surface API at the Execution Point.
With C# Interfaces the pattern surface API can be formalized
as the execution point interface.
The SPD pattern in C# has the same 'Arrow' data flow pattern as found in the
C++ example. In the C# example the data flows;
- From the (1) execution point (interface surface) to the (2) foundation (via SII)
- From the (2) foundation to the (3) execution point (via GVI)
- From the (3) execution point to the (4,5) support classes (via object aggregation)
In the C++ example the data flows;
- From the (1) execution point (interface surface) to the (2) foundation (via MII)
- From the (2) foundation to the (3) execution point (via GVI)
- From the (3) execution point to the (4,5) support classes (via MII)
The functional result, at the execution point surface, is the same for both the
C++ and the C# examples. The output of both implementations is identical.
The High State Tracker example of the SPD pattern is more reflective of real
world production software (then the shorter C# examples herein). It features
the following C# Virtual Inheritance phenomena (GVI/SVI-Interfaces).
High State Tracker Example - Code Features
Code Feature
Implementation Description
Coexistence
GVI, SVI, Interfaces, object aggregation (embedded members)
GVI Control mechanism
Virtual method specification - Base::Log_state()
GVI Implementation
Virtual method implementation - Diamond_tracker::Log_state()
SVI Specification
Interface inheritance - Execution_point_interface
SVI Parent
Test_driver::Execution_point_interface
SVI Child
Diamond_tracker
Execution Point Contract
C# Interface for the surface API of the SPD pattern used by the test driver
Object Aggregation
Via embedded members Internet_child
, Database_child
Aggregate Construction
Construction code for Internet_child
, Database_child
Surface API Replication
Diamond_tracker
must replicate Get_local_highest_state()
, Get_global_highest_state()
API surface
C# Abstract Method
The Log_state()
method is an abstract method with a parent/client use context
Restricted use context
The SVI virtual code element New_state()
can not be used outside the client use context
The C# and C++ examples help to distinguish the concepts;
- Functional normalization/dispersion/aggregation along lines of inheritance (GVI/MII)
- Object aggregation and interface specification (GVI/SII)
Comparing the architecture of virtual inheritance in both these contexts helps to understand
what benefits GVI and Interfaces (SVI) bring to the pattern functionality.
It also helps to clarify how the code element use context of virtual (VI) and implementation (II) inheritance
differs.
Relative to Virtual Inheritance (GVI/SVI)
the C# implementation of the SPD pattern differs from the C++ implementation
in several important ways. A comparative examination of the code reveals
these differences stem from the difference between GVI/MII and GVI/SII.
This difference is relevant to SVI and its role in pattern functionality.
It is important to note that the Interface SVI adds nothing to the functionality of the pattern itself.
Thus SVI is not responsible for the key differences in the two designs (C++ Vs. C#).
This highlights the contractual mechanism aspect of SVI in Interfaces
brought about by the virtual code element use context (as explained above).
It also allows SVI to be factored out in any functional partitioning
design issues (as explained below).
Coexistence of Virtual and Interface Inheritance in C#
The coexistence of
Virtual Inheritance (GVI)
and
Interfaces (SVI)
in C# is demonstrated in the High State Tracker example code presented below.
The code is basically a port to C# from the C++ example (above).
Since C# lacks
Multiple Implementation Inheritance (MII)
the design of the C# code uses Single Implementation Inheritance and
object aggregation. The functionality (the output) of the SPD pattern C# implementation is identical to the C++ example.
using System;
//
// High_State_Tracker Example
//
//#------------------------------------------------------------------------------------------------#
//# State machine :_: Manages state space - Ties all concerns together
//# Use GVI - Specify Child 'Log_State()' and use it as Virtual Method
//#------------------------------------------------------------------------------------------------#
internal abstract
class Base
{
int Current_state; // Initialize to zero (not shown in example)
//
public Base()
{
Current_state = 0;
}
//
protected void Set_state(int next_state)
{
Current_state = next_state; // Move system to the new state (primary state transition)
Console.WriteLine(" : Current state is - {0} ", Current_state );
Log_state( Current_state ); // Use GVI : Control children and keep history of state changes
}
//
public int Get_current_state()
{
return Current_state;
}
//
public abstract // Virtual Method (Shows GVI - General Virtual Inheritance)
void Log_state(int new_state); // Specify here : Instantiate in Diamond class
} // End of class
//#------------------------------------------------------------------------------------------------#
//# Global Manager :_: Logs high states to global office - manages global concerns
//# Use object aggregation to incorporate functionality in SII child class
//#------------------------------------------------------------------------------------------------#
class Internet_child
{
int Highest_global_state; // Could be set to highest Home Office State (not shown in example)
//
public void Log_to_home_office(int latest_high_value)
{
// Code to send value to home office via Internet would go here
}
//
public void Log_global_high_value(int new_state)
{
if ( new_state > Highest_global_state )
{
Highest_global_state = new_state; // Save the 'next highest state'
Log_to_home_office( new_state ); // Inform home office
}
}
//
public Internet_child() // Internet retrieval could go here
{
Highest_global_state = 70; // Init for demo : Highest 'global' state
}
//
public int Get_global_highest_state()
{
return Highest_global_state;
}
} // End of class
//#------------------------------------------------------------------------------------------------#
//# Local Manager :_: Logs high states to local home office - manages local concerns
//# Use object aggregation to incorporate functionality in SII child class
//#------------------------------------------------------------------------------------------------#
class Database_child
{
int Highest_local_state; // Could be set to highest local Office State (not shown in example)
//
public void Log_to_local_database(int latest_high_value)
{
// Code to save value in local database would go here
}
//
public void Log_local_high_value(int new_state)
{
if ( new_state > Highest_local_state )
{
Highest_local_state = new_state; // Save the 'next highest state'
Log_to_local_database( new_state ); // Save to local database
}
}
//
public Database_child() // Database retrieval could go here
{
Highest_local_state = 30; // Init for demo : Highest 'local' state
}
//
public int Get_local_highest_state()
{
return Highest_local_state;
}
};
//#------------------------------------------------------------------------------------------------#
//# Session Manager :_: Tracks high states in the current session - manages immediate concerns
//# Use GVI - Parent (Base) controls child (Diamond_tracker::Log_state())
//# Use SVI - Force child to implement surface API needed by test driver (Test_API_surface)
//# Use object aggregation to bring in local and global manager functionality
//#------------------------------------------------------------------------------------------------#
class Diamond_tracker : Base, Test_driver.Execution_point_interface
{
int Session_state; // Save las
int Highest_session_state;
//
Database_child Local_manager;
Internet_child Global_manager;
//
public override void Log_state( int new_value ) // Use GVI : Parent will control child implementation
{
Local_manager. Log_local_high_value ( new_value ); // Save if highest locally
Global_manager.Log_global_high_value ( new_value ); // Save if highest globally
}
//
public Diamond_tracker()
{
Session_state = 0;
Highest_session_state = 0;
//
Local_manager = new Database_child();
Global_manager = new Internet_child();
}
//
public void New_state(int new_value)
{
Session_state = new_value; // Move session manager to new state
if ( Session_state > Highest_session_state )
{
Highest_session_state = new_value; // Track high states for this instance
}
Set_state( new_value ); // Move system to new state via Base
}
//
public int Get_session_highest_state()
{
return Highest_session_state;
}
//
public int Get_local_highest_state()
{
return Local_manager.Get_local_highest_state();
}
//
public int Get_global_highest_state()
{
return Global_manager.Get_global_highest_state();
}
};
//#------------------------------------------------------------------------------------------------#
//# Test Driver :_: Drives the high state tracker (Diamond_Tracker)
//# Show SVI as execution point contract via the C# Interface
//#------------------------------------------------------------------------------------------------#
class Test_driver
{
//#------------------------------------------------------------------------------------------------#
//# Parent Interface :_: Specifies the API surface needed by the test driver
//#------------------------------------------------------------------------------------------------#
public interface Execution_point_interface
{
void New_state(int new_value);
//
int Get_session_highest_state();
int Get_local_highest_state();
int Get_global_highest_state();
}
//
public static void Main()
{
Diamond_tracker my_diamond; // Drive child from parent via Virtual Method
my_diamond = new Diamond_tracker(); //
//
Execution_point_interface parent_as_client_API; // Drive child from parent via Interface
parent_as_client_API = (Execution_point_interface) my_diamond; //
//
Console.WriteLine();
Console.WriteLine(" End : Highest session state is : {0} ", parent_as_client_API.Get_session_highest_state() );
Console.WriteLine(" Start : Highest local state is : {0} ", parent_as_client_API.Get_local_highest_state() );
Console.WriteLine(" Start : Highest global state is : {0} ", parent_as_client_API.Get_global_highest_state() );
Console.WriteLine();
//
my_diamond.New_state( 10 );
my_diamond.New_state( 30 ); // Highest local state at start
my_diamond.New_state( 40 );
//
Console.WriteLine();
Console.WriteLine(" End : Highest session state is : {0} ", my_diamond.Get_session_highest_state() );
Console.WriteLine(" Middle : Highest local state is : {0} ", my_diamond.Get_local_highest_state() );
Console.WriteLine(" Middle : Highest global state is : {0} ", my_diamond.Get_global_highest_state() );
Console.WriteLine();
//
my_diamond.New_state( 70 ); // Highest global state at start
my_diamond.New_state( 10 );
my_diamond.New_state( 80 );
//
Console.WriteLine();
Console.WriteLine(" End : Highest session state is : {0} ", parent_as_client_API.Get_session_highest_state() );
Console.WriteLine(" End : Highest local state is : {0} ", parent_as_client_API.Get_local_highest_state() );
Console.WriteLine(" End : Highest global state is : {0} ", parent_as_client_API.Get_global_highest_state() );
Console.WriteLine();
// Show_GVI : Show that the abstract method (AM) is accessible to clients as well as parents
my_diamond.Log_state( 99 );
//
Console.WriteLine(" AM Test : Highest session state is : {0} ", parent_as_client_API.Get_session_highest_state() );
Console.WriteLine(" AM Test : Highest local state is : {0} ", parent_as_client_API.Get_local_highest_state() );
Console.WriteLine(" AM Test : Highest global state is : {0} ", parent_as_client_API.Get_global_highest_state() );
Console.WriteLine();
}
//#------------------------------------------------------------------------------------------------#
//# Parent Access :_: Shows that compiler errors occur if virtual code element used out of context
//# Prove_SVI : Prove that the SVI virtual code elements not be accessible by parent
//#------------------------------------------------------------------------------------------------#
public void SVI_parent_access_test()
{
// Show_SVI use context : virtual code element New_state() is not directly accessible
// New_state(2); // Remove comment to see compiler error
// Show_SVI use context : virtual code element New_state() is not indirectly accessible
// : Must access as a client to some instance implementing specification
// Execution_point_interface.New_state(8); // Remove comment to see compiler error
}
} // End_of_class
//#------------------------------------------------------------------------------------------------#
//# Output :_: Show the state transitions - Highest state logged locally and globally
//# Show GVI as an internal pattern unit mechanism - Parent use context
//# Show SVI as an external pattern unit mechanism - Client use context
//#------------------------------------------------------------------------------------------------#
//
// Start : Highest session state is : 0
// Start : Highest local state is : 30
// Start : Highest global state is : 70
//
// : Current state is - 10
// : Current state is - 30
// : Current state is - 40
//
// Middle : Highest session state is : 40
// Middle : Highest local state is : 40
// Middle : Highest global state is : 70
//
// : Current state is - 70
// : Current state is - 10
// : Current state is - 80
//
// End : Highest session state is : 80
// End : Highest local state is : 80
// End : Highest global state is : 80
//
//
// AM Test : Highest session state is : 80
// AM Test : Highest local state is : 99
// AM Test : Highest global state is : 99
//
SVI Specification :
For Virtual Inheritance (VI) the pattern interface
(Execution_point_interface
) functions just like a class. The child
inherits the interface. In this example the interface is the specification of
the 'parent' (Test_driver
) to the child
(Diamond_tracker
). This shows how an interface can be used to
consolidate a set of virtual code elements (specifications via inheritance) as a
single contract between the client and
the pattern unit (SVI - client use context).
SVI Parent :
Note that the contract (Execution_point_interface
) is in the namespace of the
test driver. Syntactically the contract is the parent, not the test driver. Conceptually,
however, the interface is a specification mechanism in the design architecture that
binds the test driver to the target (Diamond_tracer
).
This design architecture is named 'parent-as-client' to note that the interface specification is located in the client class namespace.
SVI Child :
Note that the Interface (SVI) has nothing to do with the pattern functionality.
An identical functionality exists in the C++ SPD pattern unit without the SVI specification.
This shows that the effects of Simple Virtual Inheritance (SVI) can be factored
out in any functional partitioning issues (via inheritance) within the pattern unit.
Only GVI, because of its parent use context (of the virtual code elements), can
effect pattern functionality via inheritance (as is shown in the C# SPD example
above).
Execution Point Contract :
Note that the contract for the surface API of the SPD pattern unit is NOT the same
as a contract for the target functionality (within the pattern unit).
Interfaces, a form of virtual inheritance (SVI virtual code elements),
only provide an 'interface specification'. No specification of the actual functionality
(provided by the interface) of the pattern is provided.
Object Aggregation :
Since C# does not have Multiple Implementation Inheritance the example must
contain, create and 'surface replicate' functional support members
(such as methods that exist at the pattern surface API).
Aggregate Construction :
Unlike MII in C++ the Internet_child
and Database_child
require
construction code at the execution point (Construction burden of aggregates).
Surface API Replication :
Unlike MII in C++ the
Get_local_highest_state()
and Get_global_highest_state()
support methods must be replicated at the execution point (surface burden of aggregates).
C# Abstract Method : The Log_state()
abstract method can be accessed by both
the parent and the client. The client access is shown at the end of Test_driver.Main()
.
Since Log_state() is a virtual code element we can see that the use context of abstract methods
includes parents and clients (GVI/SVI). This compares with the restricted use context of virtual code
elements defined in Interfaces (SVI).
Restricted Use Context : The SVI_parent_access_test()
in the Test_driver
shows that virtual code elements defined in C# Interfaces are not accessible to parents.
Only client object access is allowed.
This can be compared with abstract methods in C++
abstract methods in C++
where the virtual code element is usable by both the parent and
client objects.
The code features of the C# SPD implementation above combine to
form a single functional unit abstracted at the execution point surface.
The functional system, formed by the pattern
participants, can be seen as a 'single balanced unit of functionality' which is the pattern
implementation (pattern unit). Balance is achieved via
Functional Normalization
among the participants.
The example above shows a common use of Virtual Inheritance (VI - code element
specification via inheritance) in the form of GVI (parent use context) and SVI (client
use context). The most important aspect presented in the example is
that SVI is external to the pattern (external contract) while GVI is internal to the pattern (internal plumbing).
Summary of Virtual and Interface inheritance in Object Oriented Languages
Virtual Inheritance (VI) is the specification of code elements via inheritance. The virtual code elements
produced by the specification have either a parent use context (GVI) or a client use context (SVI).
The use of SVI and GVI is presented in the context of Class based functional units.
In this case (class based design) the following hold true.
GVI is an intra-pattern mechanism : It provides parental control of virtual code elements (parental use context).
GVI can leverage the features of MII more than it can SII. This is because SII based
designs use object aggregation/composition which lacks the lines of inheritance needed
for GVI functionality.
SVI is an inter-pattern mechanism : It provides client control of virtual code elements (client use context).
SVI has no effects via inheritance other than the forced implementation of the specification. As such it can not
be used within the pattern unit for parental control (no parent use context).
The internal/external comparison of GVI/SVI in pattern unit design is valid for class based functional units like
High State Tracker. System level component design patterns, on the other hand, use component based participants
to form functional pattern units.
Component level patterns, which bind together complex pattern participants, may use SVI internally
as contracts between participants. These types of systems are generally loosely coupled and non-performant
in comparison with tightly coupled high performance pattern units like High State Tracker.
Use context, lines of inheritance and participant bindings (class/component) are
important foundation concepts in understanding the role Virtual Inheritance (VI)
takes in the object oriented design process.
The coexistence of GVI as internal plumbing
compliments SVI as external contract.
It is clear from the example that virtual code element design (the specification
process) starts with WHO will be using the elements. Internal parents, external
clients and participant binding are the three perspectives to keep in mind to
distinguish the architectural benefits of GVI and SVI.
Java
In Java, all class methods are virtual: no keyword is necessary. The Java translation of the C++ is:
public class Human{
public int Eyes(){ return 2; }
}
public class Mutant extends Human {
public int Eyes(){ return 3; }
}
public class Program {
public static void main(String [] args){
Mutant m = new Mutant();
Human h = m;
System.out.print(m.Eyes());
System.out.print(",");
System.out.println(h.Eyes());
}
}
The result of this program running is the output 3,3
.
Semantics
It is necessary for the terminology of object-oriented (OO) computing to
articulate the phenomena of
Virtual Inheritance
precisely. The terminology should reflect the conceptual semantics of the core
computing phenomena rather than the syntactic mechanisms of any one object-oriented
language.
Ideally the conceptual semantics and syntax mechanisms of OO code elements are
identical but this is not always the case. The rapid evolution of computer
science has accelerated the semantic evolution of its core terminology.
It has become a common industry practice, therefore, to
provide qualifiers and acronyms (if needed) for terms that vary in their
conceptual meaning due to use in a specific context (generally a particular
computer language syntax).
In
Implementation inheritance (II), for example,
there is
Note that MI (Multiple Inheritance)
can technically include Interfaces as in C#.
Thus the impetus to clarify the conceptual semantics of core terminology in the face of industrial obsolescence.
A case in point,
Virtual Inheritance (VI)
as distinguished from
Resolution Inheritance (RI)
within this WikiPedia article (see
C++ Resolution Mechanism).
In order to better articulate this difference the conceptual semantic scope and history
of object-oriented language syntactic mechanisms is briefly addressed.
The
semantic scope
and
history
of Virtual Inheritance is articulated in the sections below.
This articulation helps to clarify the 'General' aspect (GVI) of 'Virtual Inheritance' from subjugated concepts
such as Virtual Functions (as subset of GVI) and language specific meanings (i.e. C++ RI, etc)
See also
This 'See Also' topic map provides links to articles related to Virtual Inheritance (VI).
Virtual Inheritance is best understood within the context of class inheritance
(all forms of inheritance). In the same way, Class Inheritance (CI) is best
understood in the context of relevant design approaches (for combining computing
functionality), issues and problems in software architecture.
The sections listed below help to distinguish the concepts of 'functional
dispersion/aggregation along lines of inheritance' (as in Virtual/Implementation
Inheritance) from 'object aggregation/composition' as embedded/referential
members in a class (embedded/referential containment).
Note the interesting semantic distinction between
the AGGREGATION of 'self reliant' objects from the COMPOSITION of 'object parts' (from
the English language - 'composition' semantics infers 'composed of parts').
Similar subtle but important distinctions (semantic aspects) exist between
virtual, implementation, multiple (MII) and interface inheritance.
Fundamental Inheritance Concepts
This section contains links to articles relevant to
the general concept of inheritance.
Detailed Inheritance Concepts
This section contains links to articles explaining
the conceptual details of inheritance.
Other Design Approaches
This section contains links to other ways of combining
computing functionality (object aggregation/composition). Links to these concepts are
listed to contrast inheritance from other approaches to functional
design in object-oriented languages.
- Object Aggregation (via referential containment)
- Aspect-Oriented Programming (quantification/join mechanism for separation of code)
- Object Composition(via embedded containment)
- Function Composition (via sequential effects)
- Composition (disambiguation page)
- Object Association (aggregation Vs. composition semantics page)
Inheritance Issues and Problems
Many design problems in inheritance occur not because a mechanism (VI, II, MI, Interface) is 'bad' but
because the right tool (mechanism) wasn't used for the right job (like using a hammer for a screwdriver).
The problems below are not inherent to semantic mechanisms (which define the MEANING of a lanuage specification). These problems ARE, however, inherent to poor software design (actual language mechanism misuse in jave, C#, C++ etc.) or poor language
implementations (bad compiler architecture/language specification/language design).
Note that the semantics of a poorly designed 'expression mechanism IMPLEMENTATION'
(in a language specification or design) do not 'redefine' the SEMANTICS of an expression mechanism
(the inherent semantic meaning across all past, present, future and hypothetical language specifications). The 'CONCEPTUAL expression semantics' exist prior to and beyond any one language SYNTAX specification (C++, C#, Java, etc.). Most language mechanism specifications (SYNTAX EXPRESSIONS) are formalizations of previous programming practices in an earlier language (thus an evolution in expression formalization and understanding).
Note also that syntax mechanisms should not be confused with conceptual semantics. You can not take a specific language specficiation and reverse engineer its syntactic mechanisms (what the compiler SHOULD generate) into the conceptual semantics expressed in the English (Human) lanaguage.
The supposion that inherently difficult and complex processing
should suddenly become simple and trivial is also incorrect (i.e. an automobile is NOT
as complex as a nut and bolt).
In the same way, each inheritance mechanism (VI, II, MII, etc) has logical
semantic aspects that pre-exist ANY particular language implementation generated
by a compiler (cause/semantics precedes effect/implementation). This
perspective is important in distinguishing a poor software design (expression
mechanism misuse) from a poor mechansim specification/implementation (language specification problem, compiler design problem).
Design Intentions in Inheritance
Virtual Inheritance (VI) is one of many object-oriented mechanisms used to achieve a design intention.
Object-oriented software design intentions are generally guided by the principles listed below.
- Abstraction (Overview of conceptual partitioning mechanisms - class, module, etc.)
- Code Reuse (functional migration of replicated child code to parent class)
- Delegation (historical semantic issues)
- Encapsulation (disambiguation page)
- Information Hiding (principal underlying encapsulation technique in object-oriented programming)
- Separation of Concerns (no functional overlap)
Functional Normalization
Functional normalization is the removal of needlessly replicated code.
This section contains links to articles related to concepts relevant to
this process.
- Functional normalization (object-oriented programming)
- Orthogonalization (mathematical normalization)
- Aspect (functional perspectives orthogonal to core concern)
- Core Concern (the intrinsic and inherent core functionality of a class)
- Cross-cutting Concern (common functionality orthogonal to core concern)
- Refactoring (restructuring software)
- Database Normalization (minimize needlessly replicated data)
Logical Context of Virtual Inheritance
Virtual Inheritance may be categorized and classified under the following headings.