1.
Let us assume that objects are represented by references in a custom format defined by the programmer. For example, bank accounts could be represented by their numbers. In the concept-oriented programming (CoP) a special construct, called concept, is used for this purpose. Concept is a pair of two classes -- one reference class and one object class. With the empty reference class, concept is equivalent to normal classes as used in OOP. However, below we ignore properties of the object class and discuss only role of the reference class -- precisely what distinguishes concepts from conventional classes.
Reference class in concepts is a mechanism for defining custom references of objects. For example, if we need to represent account objects by account numbers (rather than by native references) then the reference class has one field which stores this account number:
concept Account
reference {
String accNo;
...
}
object {
double balance;
...
}
Notice that the name is assigned to the whole concept, i.e., to the pair of one object class and one reference class, which essentially loose their role as separate constructs and work only together in close cooperation. In other words, we do not define separately a reference class but rather simultaneously two classes with one name. It is a very important feature of the whole concept-oriented paradigm.
Now, if we use concept Account for declaring type of variables then accounts will be stored and passed by means of their numbers rather than using native references like memory address in C++:
Account account = getAccount("Alexandr Savinov"); // Account number
Here variable account stores an account number. If Account were a normal class then it would store a native reference. Thus object representation and access in CoP is indirect.
2.
Using one concept (or one class) we can define a flat address spaces where objects of one class are represented by references of the associated class (reference class from the concept). Yet frequently we need to describe a hierarchical address spaces where a reference is only one segment in the address. In other words, each segment in a hierarchical address specifies a relative position of the object with respect to the parent segment which in turn is a relative position with respect to its own parent segment and so on till the root of the address space. Such address space is analogous to ordinary postal addresses.
For modelling such hierarchical spaces, CoP uses inclusion relation, which generalizes class inheritance just as concepts generalize classes. Parent address space for this concept is specified by including it into some parent concept. For example, bank account numbers are only relative identifiers which make sense only in one bank. Hence, if we want to deal with accounts of different banks then it is necessary to define two-segment addresses where first (high) segment is bank code and the second (low) segment is account number within this bank. This can be easily done in CoP as follows:
concept Bank
reference {
String bankCode;
...
}
object {
String name;
...
}
concept Account in Bank ...
Notice that concept Account is included into parent concept Bank. As a consequence, references to bank accounts will 'inherit' all fields from the parent reference (yet, it is not so for objects). Such references are referred to as complex references. Now if we declare a variable of type Account, it will store two segments taken from reference classes of two concepts:
Account account = getAccount("Alexandr Savinov"); // Bank code + Account number
3.
This hierarchical address space can be further extended in the other direction. Assume that bank accounts have sub-accounts like savings accounts or checking accounts, which are defined only with respect to the main account. To represent and access such objects it is necessary to provide bank code, main account number and finally sub-account number. Again this can be done by including child concept into concept Account:
concept SavingsAccount in Account
reference {
String subAccNo;
...
}
object {
double balance;
...
}
concept CheckingAccount in Account
reference {
String subAccNo;
...
}
object {
double balance;
...
}
If we declare a variable of this more specific type then it will store references consisting of three segments:
SavingsAccount savingsAccount; // Bank + Account + SavingsAccount CheckingAccount checkingAccount; // Bank + Account + CheckingAccount
4.
Notice that references stored in these variables do not contain any information on the real location of the represented objects. This means that these objects can be anywhere in the world: on disk/tape in the bank, in a database, on CD, in global/local heap, on board of International Space Station or on Mars. This is why objects in CoP are said to be represented indirectly and the whole approach is devoted to providing means for indirect object representation and access (ORA).
However, in the program we manipulate objects as if they were directly accessible objects in OOP. In other words, we have a complete illusion of direct access because we still can apply methods to such references or access them in any other way. For example, we might get account balance, account.getBalance(), and here we do not care what kind of reference is stored in the variable and used for access. Thus use of objects does not depend on their representation method. If later we change the way objects are represented then the code that uses these objects remains the same.
Another important property is that inclusion is significantly different from inheritance. The thing is that inclusion means that a child element IS-IN a parent element at run-time, i.e., one parent (base) may have many children (extensions). In OOP inheritance means that a child IS-A parent element and hence extensions and bases are in one-to-one relationship. In CoP elements (objects and references) exist within a hierarchy at run-time where extensions have their own references and may share base elements. Local reference of an extension allows distinguishing it from other extensions within this base element.
The third point is that complex references need not to start from the root. If we were using inheritance for describing complex references (for inheriting base segments of the address) then all references would have global scope. In CoP it is possible to control the length (and hence scope) of references by choosing the starting segment type. It is very useful feature if we know that some reference will be used only in a restricted context. For example, it is obviously not necessary to store bank reference segment if the account is used within this bank only. This mechanism of reference length control will be described in future posts.
