3D PLM Enterprise Architecture |
Middleware Abstraction - Object Modeler |
Object Modeler InheritancesFactorizing and reusing your code |
Technical Article |
AbstractThis article explains the Object Modeler (OM) inheritance for interfaces and for components. |
CAA V5 offers some powerful means in addition to declaring and implementing interfaces. You can take advantage of interfaces already implemented in existing components to reuse existing code, and to allow for scalability, bringing new developments without overwriting the whole code or rebuilding all existing client applications. These means are interface inheritance, component inheritance, both being Object Modeler inheritance, extensions and the way to share extensions between components.
Inheritance is one of the major elements of the object model. With common object-oriented languages like C++, inheritance brings to a class the type of its base classes. This means that the derived class inherits their data members methods. The same mechanism applies for interfaces and for their implementations within CAA components.
[Top]
Object Modeler (OM) interface inheritance is one of the main means to factorize behavior. Remember that an interface brings to a component one of its behavioral facets rather than only a set of methods. Deriving an interface from another one adds to the current interface the behavior exposed by the base interface to make a more complex, specialized, or comprehensive behavior.
Assume that the CAAIXX interface derives from the CAAIAA
interface. This derivation must be altogether a C++ derivation between the
abstract classes that stand for the interfaces, and an OM-derivation
stated using the CATImplementInterface macro. A component
implementing CAAIXX gets the behavior provided by the inherited CAAIAA
in addition to the behavior specific to CAAIXX. On the figure
beside, the CAACmp component which implements CAAIXX among
other interfaces inherits the support of CAAIAA from CAAIXX.
This means that QueryInterface returns S_OK when
asked for CAAIAA using a pointer to another interface implemented
by CAACmp, such as CAAIYY, without CAACmp needs to
declare implementing CAAIAA (no TIE, no dictionary entry.)
Nevertheless, CAACmp needs to provide the body of the MAA1
and MAA2 methods. |
Assume that the CAACmp class, that is the CAACmp component main class, implements itself the CAAIXX and CAAIYY (usually, interfaces are im^plemented using extension classes, and the component main clas is quite empty.) The CAACmp class source file could be as follows:
... #include "TIE_CAAIXX.h" #include "TIE_CAAIYY.h" ... CATImplementClass(CAACmp, Implementation, CATBaseUnknown, CATNull); ... TIE_CAAIXX(CAACmp); // No TIE to declare CAAIAA TIE_CAAIYY(CAACmp); // implementation ... HRESULT CAACmp::MAA1() // CAAIAA methods need to be { // implemented ... } HRESULT CAACmp::MAA2() { ... } ... |
A client application typical usage of interface inheritance could be as follows:
... HRESULT rc = E_FAIL; IUnknown *piUnknownOnCmp = NULL; rc = ::CATInstantiateComponent("CAACmp", IID_IUnknown, (void **) &piUnkownOnCmp); if ((SUCCEEDED(rc) && NULL != piUnkownOnCmp) { CAAIYY *piCAAIYYOnCmp = NULL; HRESULT rc = piUnkownOnCmp ->QueryInterface(IID_CAAIYY, (void**) &piCAAIYYOnCmp); piUnkownOnCmp ->Release(); if (SUCCEEDED(rc)) { CAAIAA *piCAAIAAOnCmp = NULL; rc = piCAAIYYOnCmp->QueryInterface(IID_CAAIAA, (void**) &piCAAIAAOnCmp); piCAAIYYOnCmp->Release(); if (SUCCEEDED(rc)) { piCAAIAAOnCmp->MAA1(); piCAAIAAOnCmp->MAA2(); piCAAIAAOnCmp->Release(); } } } ... |
The component is created using the CATInstantiateComponent
global function which returns a pointer to IUnknown. Then a pointer to CAAIYY
is asked, and using this pointer, a pointer to CAAIAA is asked and
successfully returned. It can be used as if CAACmp were implementing CAAIAA,
when it only implements CAAIAA methods and inherits CAAIAA
support from CAAIXX.
In addition, a pointer to the derived interface CAAIXX can be
used to call the MAA1
and MAA2
methods of the base
interface CAAIAA in addition to its own methods MXX1
and MXX2
, as shown in the following example.
... CAAIXX *piCAAIXXOnCmp = NULL; HRESULT rc = piCAAIYYOnCmp->QueryInterface(IID_CAAIXX, (void**) &piCAAIXXOnCmp); if (SUCCEEDED(rc)) { piCAAIXXOnCmp->MXX1(); piCAAIXXOnCmp->MXX2(); piCAAIXXOnCmp->MAA1(); piCAAIXXOnCmp->MAA2(); piCAAIXXOnCmp->Release(); } ... |
[Top]
A component that implements interfaces can OM-derive from another component that itself implements other interfaces. The derived component can be seen as a kind of the base component from which it derives, since it has the behavior of this component in addition to its own behavior. This is called OM-inheritance. It has nothing to do with C++ inheritance, although there are some similarities. OM component inheritance allows for code reusability and factorization.
The OM-inheritance brings to CAADerivedCmp the ability to execute the methods of the inherited implementation of the CAAIXX and CAAIYY interfaces, in the same way than any C++ derived class instance can execute the methods of its base classes. | |
The difference is that there can be no C++ inheritance links between the
classes that make up CAADerivedCmp and those that make up CAABaseCmp,
except between the component main classes, that is, those that are
declared using the keyword Implementation in the CATImplementClass
macro: the CAADerivedCmp component main class must C++-derive from
the CAABaseCmp component main class. The OM component inheritance
makes CAADerivedCmp implement the interfaces implemented by CAABaseCmp
without explicitly declaring that CAADerivedCmp implements these
interfaces (no TIE, no dictionary entry), and without providing the code
for their methods. In other words, the OM component inheritance allows QueryInterface
to return S_OK and a valid pointer to CAAIXX, for example, when it
is called using a pointer to CAAIZZ on CAADerivedCmp. This
is expressed thanks the macro CATDeclareClass in the CAADerivedCmp
main class header file, and thanks to the CATImplementClass
macro in the associated source file. This class name must be the component
name, that is, CAADerivedCmp. |
The CAADerivedCmp class header file is as follows:
... #include "CAABaseCmp.h" ... class CAADerivedCmp : public CAABaseCmp { CATDeclareClass; public : CAADerivedCmp(); virtual ~CAADerivedCmp(); private : CAADerivedCmp(const CAADerivedCmp &iObjectToCopy); }; |
And its source file is as follows:
... #include "CAADerivedCmp.h" ... CATImplementClass(CAADerivedCmp, Implementation, CAABaseCmp, CATNull); CAADerivedCmp::CAADerivedCmp() {} CAADerivedCmp::~CAADerivedCmp() {} |
The CATImplementClass
macro states that the class CAADerivedCmp
is a component main class thanks to the Implementation
keyword, and
that the component OM-derives from the component whose main class is CAABaseCmp.
The fourth argument must always be set to CATNull
for component
main classes. The class includes only a constructor and a destructor. A copy
constructor is declared in the class private part and is not implemented. This
prevents the compiler from creating the copy constructor as public without you
know.
The CAAExtZZ extension class header file is as follows:
... #include "CATBaseUnknown.h" ... class CAAExtZZ : public CATBaseUnknown { CATDeclareClass; public : CAAExtZZ (); virtual ~CAAExtZZ (); HRESULT MZZ1(); HRESULT MZZ2(); private : CAAExtZZ(const CAAExtZZ &iObjectToCopy); }; |
The CAAExtZZ extension class header file is as follows:
... #include "CAADerivedCmp.h" ... CATImplementClass(CAAExtZZ, DataExtension, CATBaseUnknown, CAADerivedCmp); #include "TIE_CAAIZZ.h" TIE_CAAIZZ(CAAExtZZ); CAAExtZZ::CAAExtZZ () {} CAAExtZZ::~CAAExtZZ () {} HRESULT CAAExtZZ::MZZ1() { ... } ... HRESULT CAAExtZZ::MZZ2() { ... } |
The CATImplementClass
macro states that the class CAAExtZZ
extends the CAADerivedCmp, thanks to the DataExtension
keyword. No OM-inheritance can be set to an extension, and the third argument
must always be set to either CATBaseUnknown or CATNull
. The
class includes a constructor and a destructor. A copy constructor is declared in
the class private part and is not implemented. This prevents the compiler from
creating the copy constructor as public without you know. In addition, it
implements the CAAIZZ methods.
Let's see how a client application can make use of OM component inheritance.
... HRESULT rc = E_FAIL; IUnknown *piUnknownOnDerivedCmp = NULL; rc = ::CATInstantiateComponent("CAADerivedCmp", IID_IUnknown, (void **) &piUnkownOnDerivedCmp); if (SUCCEEDED(rc) && NULL != piUnknownOnDerivedCmp) { CAAIZZ *piCAAIZZOnDerivedCmp = NULL; rc = piUnknownOnDerivedCmp->QueryInterface(IID_CAAIZZ, (void **) &piCAAIZZOnDerivedCmp); piUnknownOnDerivedCmp->Release(); if (SUCCEEDED(rc)) { piCAAIZZOnDerivedCmp->MZZ1(); piCAAIZZOnDerivedCmp->MZZ2(); CAAIXX * piCAAIXXOnDerivedCmp= NULL; rc = piCAAIZZOnDerivedCmp->QueryInterface(IID_CAAIXX, (void **) &piCAAIXXOnDerivedCmp); piCAAIZZOnDerivedCmp->Release(); if (SUCCEEDED(rc)) { piCAAIXXOnDerivedCmp->MXX1(); piCAAIXXOnDerivedCmp->MXX2(); piCAAIXXOnDerivedCmp->Release(); } } } ... |
As you can see, even if CAADerivedCmp doesn't implement the methods of
CAAIXX, it supports this interface. This support is due to the OM
component inheritance. Note also that although the CAAIZZ interface is
implemented by the CAAExtZZ extension class, this class is never
explicitly handled. The CAADerivedCmp component is instantiated using the
CATInstantiateComponent
global function and handled first as a
pointer to IUnknown, and pointers to the interfaces it implements can
then be retrieved, whatever the actual C++ classes that make up the component
and that contain the methods' code. The component remains a black box for the
client application.
The Object Modeler inheritances deal with interfaces and components.
Interfaces that OM-derive must also C++ derive, while this makes sense only for
component main classes. OM-inheritance is declared using the CATDeclareInterface
and CATImplementInterface
macros for interfaces, and using
the CATDeclareClass
and CATImplementClass
macros for
components in the component main class, and in the component extension classes
as well.
OM-inheritance for interfaces enables the derived interface to be seen as its base interface.
OM-inheritance for components enables the derived component to expose the inherited interfaces and their implementations as if they were its own.
[1] | What Is HRESULT? |
[2] | About __stdcall |
[3] | About Globally Unique IDentifiers |
[Top] |
Version: 1 [Jan 2000] | Document created |
[Top] |
Copyright © 2000, Dassault Systèmes. All rights reserved.