3D PLM Enterprise Architecture

Middleware Abstraction - Object Modeler

Object Modeler Inheritances

Factorizing and reusing your code
Technical Article

Abstract

This article explains the Object Modeler (OM) inheritance for interfaces and for components.


Code Reusability and Factorization

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 Interface Inheritance

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]

Object Modeler Component Inheritance

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.


In Short

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.

Top


References

[1] What Is HRESULT?
[2] About __stdcall
[3] About Globally Unique IDentifiers
[Top]

History

Version: 1 [Jan 2000] Document created
[Top]

Copyright © 2000, Dassault Systèmes. All rights reserved.