Bonchev Information Technologies and Systems
Software for organizations and people.
The Object Contextualization Model - home Hi guest
Sign up - Login
Object Contextualization Model Home How to Use Download License Text Awards & Reviews  
Object Contextualization Model Icon

Powerful and elegant methodology that expands the Object Oriented Paradigm and increases the quality code.

Open Source License
Before using the Object Contextualization Model in any way you must agree with the Software License.
Synopsis
The Object Contextualization Model extends the Object Oriented Paradigm by appending context to objects at the point of their definition. The context is represented by metadata, so that the objects with and without context have the same footstep and performance. Context definitions have no size and complexity limits, and thus they could encompass large composite meanings. Based on the context of an object, the compiler is able to detect and decline incorrect assignments, for example bool_Is_Monday = bool_Is_Fish. Further, by attaching context to function parameters, the Object Contextualization Model removes the limitation of one function overload per prototype per namespace, since even though parameters may be of the same intrinsic type, the different context makes them differentiable and unambiguously referable. The latter is particularly helpful for operator overloading since operators have a fixed number of parameters, and up until now multiple behaviors of operators could have been achieved only by means of inheritance. The Object Contextualization Model also promotes a more structured class model of the software, and helps to maintain code that is easier to understand. However, the Object Contextualization Model does require more typing while programming.
Introduction
It is not unusual for people to wonder why, in the law of God, lies are forbidden. One possible answer is that in an absolute world, lies cannot exist. If they do exist, the world is not absolute. This is so because for a lie to exist, it requires another lie to maintain it and thus if a lie is introduced then eventually the world becomes entirely self-contradictory. Hence in an absolute world, which by definition is complete and omniscient, a lie is impossible. Conversely, in an omniscient world lies naturally cannot exist. Take this affirmation and translate it to the relationship between a world that has been modeled by software and the world of the software itself. The software must represent the universe of discourse precisely, or else sooner or later at least one contradiction will appear, making it eventually impossible for the software to be maintained. This implies necessity for the software model to be in one to one correspondence with the world that it describes, i.e. to be free of "lies". In addition, names of types and objects must be precise. The latter is only required to help avoid misunderstandings and the use of incorrect entities, which leads to errors and "lies".
Object Contextualization Model
We consider software modeling in an object oriented environment, using C++ in particular.

Suppose that the model of software representing a particular Universe of Discourse is correct and that all types and objects in it are named correctly. What makes it impossible to erroneously use two objects of the same type interchangeably? For example, suppose that there are two Boolean variables representing the values of two unrelated propositions, say: "The sky is blue." and "The sea has a bottom." Clearly, they are not directly related and must not be confused or mixed with one another. However since the two variables are of the same type (Boolean), it is possible to erroneously assign them one to the other, or use them inappropriately in a function call, one of them instead of the other. Thus although the variables are well designed, errors are possible.

Thus, we observe the existence of another space in which each and every variable exists and can only be used, which is its context. The context, or the space where a variable is meaningful, clearly exists across the model and its various entities and layers. We will sometime refer to the context as context-space.

Definition: Context of an object is the space where it has intrinsic meaning.

Context information can be used by the compiler to ensure that the variables are being used only in their correct context; the context-space in which they exist. Variables from different contexts could not be used cross-contextually, except through appropriate explicit context conversion. Presently, only the name of the variable possibly records its context, e.g. boolIsTheSkyBlue or boolDoesTheSeaHaveBottom, and it is the obligation of the software engineer to ensure that context borders have not been violated.


Proposal: The author hereby proposes the idea to attach the context of an object to it, and thus make it impossible to violate context-space boundaries by mixing variables from different contexts.


The property maintaining the context information must be part of the meta-space, as opposed to part of the object. Thus, the context of the object should not in any way alter the physical footstep of the object or its performance. All context information, checks, conversion and other properties and information must be maintained explicitly and only in the meta-space and have no impact whatsoever on the compiled software and its performance.

Besides through the name of a type, context is already added to types through the namespace where the type is defined, its container (if nested), its ancestors and the overload constructors, methods and conversion operators. In respect to variables, presently context is also added to them through their names, at a physical level via place of instantiation and scope, and access modifiers. The proposal in this paper is to add logical scope to objects, i.e. context, and thus prevent their misuse in the same way as object from incompatible types cannot be misused without explicit conversion. This will have other positive consequences as we shall see.

Adding context to an object of a class must modify the methods of the class in a consistent manner. Ideally there would be native compiler support, where appropriate syntax might be, for example:
    bool<~ ContextA ~> bObjectName1;
    bool<~ ContextB ~> bObjectName2;

Definition: Adding context to an object at its definition is called object context-specialization or contextualization.

It is possible to achieve object contextualization, i.e. attach context to variables with the facilities that C++ already has, although native support is preferable. By using template specialization and inheritance, we are able to achieve the required results. Consider the template class definition from Listing 1 below.


Listing 1. Template class achieving object contextualization using already available C++ facilities.
    template< class classConstitution, class classContext > class contextualize : public classConstitution
    {
    public:
       class Init
       {
       public:
          const classConstitution& obj;

       public:
          explicit Init( const typename classConstitution& obj ) : obj( obj )
          {
          }

       private:
          Init& operator=( const Init& objInit )
          {
            obj = objInit;

            return( *this );
          }
       };

       static Init Initialize( const typename classConstitution& obj )
       {
          return( Init( obj ) );
       }

       contextualize()
       {
       }
   
       contextualize( const Init& obj ) : classConstitution( obj.obj )
       {
       }
   
       contextualize( typename const contextualize< classConstitution, classContext >& obj ) :
                                                                    classConstitution( *(classConstitution*)&obj )
       {
       }


       typename contextualize< classConstitution, classContext > operator=( typename const 
                                                               contextualize< classConstitution, classContext >& obj )
       {
          __super::operator=( obj );

          return( *this );
       }


       // Need to add the full spectrum of operators in future.
       typename contextualize< classConstitution, classContext > operator!() const
       {
          return( contextualize< classConstitution, classContext >( 
                                   contextualize< classConstitution, classContext >::Init( __super::operator!() ) ) );
       }


       typename classConstitution& GetConstitutionObject()
       {
          return( *this );
       }


       typename const classConstitution& GetConstitutionObject() const
       {
          return( *this );
       }
    };


    #define as ,
    #define by ,
    #define of ,

Remark: We do not make a distinction between built-in type and user-defined type. For the purposes of this proposal, we shall supply a wrapper class to any used build-in type in order to maintain consistency.

Figure 1 below depicts the class diagram of the contextualize template class.

Figure 1. Class diagram of the contextualize< parameters > template.
Object Contextualization Model Class Diagram


An object from type "contextualize" is specialized with a classConstitution type that we want to be the intrinsic for the newly created object type, and a second specialization classContext type giving the context, which achieves our objective. An object defined and context-specialized (contextualized) with the help of this template is less attractive than what native implementation would be, but still gives the required results. For example, two Boolean variables contextualized with ContextA and ContextB respectively, would look like:
    contextualize< Bool, ContextA > bObjectName1;
    contextualize< Bool, ContextB > bObjectName2;
where Bool is a wrapper class for bool produced to allow inheritance. In the template< class classConstitution, class classContext > class contextualize; definition above we allow only one assignment using exactly the same contextualized type. This may seem overly restrictive, but it is necessary. The problem may seem to be that an assignment such as: bObjectName1 = true/false; generates an error since the compiler does not know how to assign the reference-able (R) value to the locatable (L) value. Adding the banned operator= resolves this issue, in fact not an issue, but this clears the path for assignments such as: bObjectName1 = bObjectName2; although they have different contexts. Through implied conversion to the ancestor type (classConstitution) allowed by the public visibility of the superclass, and then through the overloaded assignment taking one parameter reference to classConstitution, the compiler can quietly assign incompatible-by-contextualization, but compatible by classConstitution variables. But this is exactly what we want to be impossible, and thus to break this chain we either have to disable the implied conversion to the superclass, or remove the operator=( typename const classConstitution& ), or both. However, when working with existing code there would be many instances of objects with type classConstitution already existing in the code as well as functions with the same type of parameters. Prohibiting the implicit conversion from the contextualized object to its intrinsic plain type (classConstitution) would lead to cumbersome programming since there will be a need to explicitly convert every assignment of contextualized object to non-contextualized new or already existing objects. On the other hand, it is most important to ensure that invalid assignment is impossible. Thus, the solution is to maintain public inheritance of the superclass and prohibit the compatible operator=( typename const classConstitution& ) and the constructor with the same prototype.

If we take the example implementation from Figure 1 as fundamental, we can make the following definitions:

Definition: Constitution class is the intrinsic type of an object.

Definition: Context class is the type which is used to contextualize the constitution object. The context class defines the context-space for the newly created object.

In this line of thought, using these definitions, we observe that Object Contextualization is very similar to choosing an element from the Cartesian product on the set of types in the model. What is more is that the selected product is still a member of the same set, namely: Let T be the set of types in a software model, then the type of contextualized object is:

TxT = { (t1, t2 ) : t1, t2 ∈ T } ∈ T.


Although the product is the type of an instance and is not explicitly defined in the set of types, it is indeed a type member of that set as we can easily reproduce it in the contextualization of another object. For example, an object of type t3 can be contextualized with the product t1 being contextualized with t2, as follows:

T x (TxT) = { (t3, (t1,t2)) : t1, t2, t3 ∈ T } ∈ T,

where the result type is again member of T for the same reasons. Thus we see that each type used to contextualize another, defines a vector, or context-space, which can be single or multidimensional.


The removal of the assignment operator with parameter reference of constitution type, however, makes it impossible to assign an object of constitution type to the contextualized object, e.g.:
    bObjectName1 = true;  // generates error
    bObjectName2 = false; // generates error

Further, we cannot introduce a constructor with parameter of type constitution, i.e. contextualized( typename const classConstitution& );, as this will lead to the same problem as with the operator=, namely bypassing the context-wall which we aim to create. Thus we introduce the auxiliary type Init and a helper function Initialize( ... ) to help the initialization. Example code looks as follow:
    bObjectName1 = contextualized < Bool, ContextA >::Init( true );
    bObjectName2 = contextualized < Bool, ContextB >::Init( false );

    // or

    bObjectName1 = contextualized < Bool, ContextA >::Initialize( true );
    bObjectName2 = contextualized < Bool, ContextB >::Initialize( false );

Example: Using classes bool and Color, and a pseudo-native compiler support we define contextualized types:
    bool<~ Color ~> boolIsMyPulloverBlue;
    Color<~ bool ~> argbColorSuitable;


Using the template from Figure 1, and assuming that Bool, ContextA and ContextB are existing types:
    contextualize< Bool, ContextA > bObjectName1( contextualize< Bool, ContextA >::Initialize( true ) );


Notice how in the next example we create a second level of contextualization.
    contextualize< Bool, contextualize< ContextA, ContextB > > bObjectName1(
                                     contextualize< Bool, contextualize< ContextA, ContextB > >::Initialize( true ) );


Remark: The definition of context-specialized type does not in any way restrict the complexity of the constitution and context classes. Thus both constitution and context classes can be contextualized types themselves to any, but finite degree.

Consider the task of placing the description of file type in a string. Some software architects may choose to create a new type derived from string especially for this purpose e.g.:
    class FileTypeDescription : public string
    {
       // do almost nothing here, most work is in the base class
    };

In practice, most architects will choose to use the string class just as it is. This of course opens them to errors from inconsistent assignments as we discussed earlier. However there is also another way. We can create an auxiliary type with an empty content but appropriate name e.g.:
    namespace sX
    {
       class FileTypeDescription { /*nothing*/ };
    };

Then we simply context-specialize the string object with it, achieving the desired result:
    contextualize< string, sX::FileTypeDescription > strType(
                                contextualize< string, sX::FileTypeDescription >::Initialize( TEXT( "File type" ) ) );

We define several pre-processor definitions for appropriate prepositions such as "as" and "by" to replace the coma in the object contextualization declarations. Thus from now on we shall use the appropriate preposition instead of coma in order to improve readability. It is good practice to declare the empty context-specialization classes defined for the sole purpose of contextualizing objects in an isolated namespace separating them from the rest of the model, in order to protect it from cluttering. By convention, this name space is called sX. Declaring and using auxiliary empty context-specialization classes is only appropriate when proper class representing the context does not already exist, and is not otherwise required. For example, suppose that the non sX-classes FileType and Description exist. Then we could do the same declaration as above as follows:
    contextualize< string as contextualize< FileType by Description > > strType;

If File, Type and Description classes exist in the model, then we could construct the context:
    contextualize< string as contextualize< Description of contextualize< Type of File > > > strType;

Most classes in a comprehensive model would exist as non sX, and very few sX classes will be required. When using multiple nested context-specialization, as in the above two examples, some disputes may arise as to which class should be constitution class and which context class, e.g. should we have contextualize< FileType by Description > or contextualize< Description as FileType >. Further, consider the following prototype:
    Pair< contextualize< Integer as sX::Quotient >, contextualize< Integer as sX::Reminder > > Integer::operator/(
                                                                                Integer iDividend, Integer iDivisor );

This declaration requires classes Quotient and Reminder to exist. On the other hand, Quotient and Reminder are integers, so there is a legitimate question whether defining class Quotient : public Integer { }; and class Reminder : public Integer {};, or even template< class T > class Quotient : public T {}; and template< class T > class Reminder : public T {}; is not better approach than using the Object Contextualization Model? Although defining Quotient and Reminder as templates may seem similar to the original declaration, they are different: contextualize< Integer as Quotient > contextualizes an Integer variable as quotient limiting its external friends and field of connections, keeping the nature of the object to be just an integer. However, the alternative yields to Pair< Quotient< Integer >, Reminder< Integer > > operator/( Integer iDividend, Integer iDivisor ); where the type Quotient is not restricted to only the whole part of division, but in a larger generalization (besides general homonyms). Quotient can also be a set, group, space, etc, and template specialization would not typically (if ever) represent this larger generalized type as the reminder from the integer division case. Further, there is no intrinsic way to prohibit self-contradictory declarations such as Quotient< double >. Another possible way is to return a rational number. Thus, unless the Quotient and Reminder are well-defined in a complete schema of hierarchic types, a context-specialization approach using dummy sX types is the better approach. Although this analysis could be taken to a deeper level we will terminate it at this point as with it we only aimed to demonstrate that careful attention is required. The most important consideration as always is to maintain a bijective relationship between the Universe of Discourse and the software model.

Contextualizing an enumerator, e.g.
    enum Result
    {
       Success   = 0,
       Failed    = 1,
       Exception = 2
       // ...
    };
is no different than contextualizing an integer, e.g. int<~ Width ~>, or an object of the respective enumerator type when native support for the Object Contextualization Model is provided by the compiler. In the example above, the contextualization of an enumerator might look as follows:
    Result<~ sX::Item ~> resultItem1; 
    Result<~ sX::Item ~> resultItem2;

Using the substitute methodology explained earlier we have to define a class for each enumerator, which would include the native enumerator and operate on its behalf, similarly to the Bool wrapper class since regretfully in C++ built in types cannot be inherited.
Protected Object Assignment
In the next example we will protect the FILETIME members of a structure from out of context assignment. Suppose that we have a structure and a function as defined below.
    struct FileAttributes
    {
       unsigned __int64 uiFileSize;
       DWORD dwAttributes;

       contextualize< FILETIME as sX::CreationTime   > ftCreationTime;
       contextualize< FILETIME as sX::LastAccessTime > ftLastAccessTime;
       contextualize< FILETIME as sX::LastWriteTime  > ftLastWriteTime;

       string strFilepath;
    };

    void main()
    {
       // Example using contextualized data members.
       // Create two objects and initialize them with zeroes.
       FileAttributes faFileA = { 0 }, faFileB = { 0 };


       // ... some code to initialize the "faFileA" and "faFileB" objects.


       // OK call - assignment in the same context.
       faFileA.ftCreationTime = faFileB.ftCreationTime;


       // This line will generate an error.
       // faFileA.ftLastWriteTime = faFileB.ftLastAccessTime;


       // OK call - cross assignment after explicit conversion.
       faFileA.ftLastWriteTime = contextualize< FILETIME as sX::LastWriteTime  >::Init( faFileB.ftLastAccessTime );


       // Context-specialize the object itself, which has contextualized members.
       contextualize< FileAttributes as contextualize< sX::Video by sX::Description > > faOfFileWithVideoDescription;
    };

Whether an object is declared in a function or a class body, or in a global scope, by adding context to it, the Object Contextualization Model protects the object from being used out of its context. More examples are given in the examples of use section.
Protected Function Calls
The Object Contextualization Model increases the quality of code by preventing the use of incorrect variables when they are from the same type but different contexts. Consider the following example taken from the source code of an earlier version of the Act On File software:
    bool Save( const bool bKeepOnTopC,
               const bool bKeepOnTopO,
               const bool bUseFullPath,
               const bool bPreviewFile,
               const bool bShufflePlay,
               const bool bPlayNextDir,
               const bool bLoopDirDirs ) const throw();

Functions with large list of parameters are not unusual in professional software development. Regardless of whether this large list of parameters is intrinsic to the function or is due to bad design, the possibility of an error occurring due to using the incorrect Boolean value exists. To prevent from this type of errors occurring, the software engineers are required to be extra careful. Using the Object Contextualization Model, we transform the above function to a safe one as follows:
    bool Save( const contextualize< Bool as sX::OnTopCompDlg > bKeepOnTopC,
               const contextualize< Bool as sX::OnTopOpenDlg > bKeepOnTopO,
               const contextualize< Bool as sX::UseFullPath  > bUseFullPath,
               const contextualize< Bool as sX::PreviewFile  > bPreviewFile,
               const contextualize< Bool as sX::ShufflePlay  > bShufflePlay,
               const contextualize< Bool as sX::PlayNextDir  > bPlayNextDir,
               const contextualize< Bool as sX::LoopDirDirs  > bLoopDirDirs ) const throw();

Although effectively all passed parameters to this function are Boolean, they must match the context of the parameter receiving it, or be explicitly converted to it.
Context-Overloading
Suppose that we have a class with the following declaration:
    class MyClass : public MyClassParent
    {
       // ...

       virtual bool operator==( const string& strFileNameToCompare ) const;
    };

Suppose the class is well-defined and working, and is derived from a whole hierarchy of types. Suppose also that there are multiple instances of it and multiple calls to the above comparison operator on them. The parameter passed as a string to the operator== is interpreted as a filename and is used to compare the content of the file with the content of the particular object on which the method is invoked. Suppose that later we discover the need to have another operator with the same prototype:
    virtual bool operator==( const string& strStringToCompare ) const;

This time we need to compare the actual string passed as a parameter to the content of the object. However, since the prototype is already used, the new function cannot be implemented with the same prototype. Traditionally the solution would be to add another parameter, thus changing the prototype and clarifying to the compiler which function we would like it to call. It is clear that such an approach is crude, but in this particular case it is not even possible since operator== can has strictly defined number of parameters. There are also other possible crude solutions, requiring flags, initializations, definition of new, not necessarily desired types, etc. The Object Contextualization Model however presents a quick and elegant solution, which is to context-specialize the parameter of the function as follows:
    class MyClass : public MyClassParent
    {
       // ...

       // original declaration - could be also kept if needed.
       // virtual bool operator==( const string& strFileNameToCompare ) const;

       // The modified original declaration.
       virtual bool operator==( const contextualize< string as sX::FileName >& strFileNameToCompare ) const;

       // The newly added function.
       virtual bool operator==( const contextualize< string as string >& strStringToCompare ) const;
    };

Using the Object Contextualization Model we are able to contextualize any parameter as we wish, including multi-level nested context-specializations. Thus the Object Contextualization Model introduces a new third way of function overloading, which are:
  1. Overriding – when using polymorphic functions with the same prototype.
  2. Overloading – when using functions with the same name but different parameter list.
  3. Context-overloading – works on both polymorphic and non-polymorphic functions with the same name and the same parameter list where only the context class of one or more parameters is different than that of any other overload. Example: the following three context-overloads are well defined overloads which will be distinguished by the compiler and called as appropriate according to the type of the second parameter of the call.
    void MyClass::MyFunction( int, contextualize< string, sX::FileName >& );

    void MyClass::MyFunction( int, contextualize< string, sX::FolderName >& );

    void MyClass::MyFunction( int, contextualize< string, string >& );
Final Remarks
We finish the Object Contextualization Model with a note that in some cases, contextualization may be needed on a verb as opposed to on a noun. That is the constitution class represents a verb or other type of entity instead of a noun as usual. The same is also true for the context class. This raises numerous questions which we leave to the reader, and may address in another paper. The ability to construct multi-layer nested contextualized types and objects, and the ability to use nouns and verbs means that an object could be contextualized with an entity, a predicate, a partial or complete sentence and even, if necessary, a paragraph, or a larger entity, as well as that any of those can be contextualized.
Conclusion
By adding context to objects the Object Contextualization Model enhances the Object Oriented paradigm in several ways:
  1. The Object Contextualization Model helps to prevent errors where objects from the same type but incompatible contexts are assigned one to another. This also applies to parameters in function calls.
  2. The Object Contextualization Model introduces context overloading which removes the one overload per prototype per namespace limitation.
  3. The Object Contextualization Model produces self-explanatory code since the declaration of a context-specialized entity embodies its context within it, which makes the context of the object clear and consistent throughout the code. In addition, the constitution and context classes' information is displayed by the IntelliSense of the IDE and also in the watch windows of the debugger, making the code writing and debugging more efficient.
  4. The Object Contextualization Model stimulates a more comprehensive class model of the software. This leads to higher quality of the software when the class model is correct and faster detection of erroneous class model otherwise, in either case this helps to achieve the desired bijective relationship between the model of the software and the Universe of Discourse.
Miroslav B. Bonchev

Update: 21st October 2012
Original post: 4th May 2011
London, England
How to Use the Object Contextualization Model
Download the Object Contextualization Model
Community Content
(To enter your comments you must be signed in. Log in or create FREE account.)
MemberComments
Be the first to comment.
Products
The ELIAS Project
Fine Art App
Information Presenter
Act On File
Audio Control
Photo Window
Information Presenter
Homepage
for Museums and Art galleries
for Schools and Universities
for Resorts, Hotels and Cruises
for Parks of any kind
for Corporations
for any business
Learning
Encryption and Authentication
Safe Online Communication
Website Testimonials
Learn how to store private keys
Make The Most From Your Files
Convenient Volume Control
Photo Window - an Awesome Gift
Support
My Account
FAQ - Forum
 
Community
Blog
Email this page
Newsletter
Bonchev IT
About
Contact
Download
Public Authentication Key
Public Encryption Key

Sitemap
Disclaimer
Privacy
Antispam
© Copyright 2024 Bonchev Information Technologies. All Rights Reserved.
Machine translation:
Search: 


Email this page
To:
use semicolon to separate emails eg: joe@abc.com; lea@abc.com
Subject:
Message:
a link to this page will be automatically added to your message
From:
Please type the anti-bot text below.
Type text:
Thank you for subscribing to the MBBSoftware newsletter.
Enter your email address:
Please type the anti-bot text below.
Type text: