[RU]

The Static Goes Dynamic

Object-oriented programming has lost much of its initial zeal and become just one of the possible programming styles. There are new fashion tricks that promise to make the world happy in a unique and inescapable way; let them cool down a little bit and take their due place in the whole. Anyway, most modern programming languages include an object-oriented subset, needed at least to attract new customers from the ranks of former competitors.

In reality, the object-oriented style is an eclectic mix of the two basic paradigms, an object and a class. Objects are basically pieces of data (properties) and code (members); classes are, in fact, the code libraries used to access the properties and members of an object treated as an instance of the class (though the same object can, of course, be treated as an instance of another class, or as a singleton, or anything). On the language level, both objects and classes provide separate namespaces for their components; in a statically compiled code, this function is almost entirely suppressed.

The traditional imperative style is thought to be an opposite of the object-oriented approach. Basically, an imperative code is a sequence of (probably concurrent) function calls (operators) acting in the context of some input data and system environment and possibly accepting a number of parameters as an individual context (e.g. closures). It does not take much effort to introduce namespaces in imperative programming as well, using special naming conventions, blocks and modules, pre-compilers etc. The degree of encapsulation does not depend on the chosen approach; there are object-oriented languages with no encapsulation at all, while imperative code can be split into fully isolated fragments interacting trough a standard interface.

Both object-oriented and imperative styles are hierarchical. Objects consist of other objects; classes inherit from other classes; functions can call other functions.

There is no principal difference between imperative and object-oriented styles. However, in strongly typed languages (like Java, or C#), the idea of a class is exaggerated to absurdity, so that any data and code at all can only be introduced through a class. Such a rigor has no reasonable grounds, while resulting in a cumbrous and ugly language lacking expressiveness and transparence.

The admission of static properties and members is one of the most obvious deviations from the object-oriented ideology. In reality, this is nothing but the way to feed us non-object entities through the asshole. There is absolutely no difference between (assuming no particular language)

class QQ { static public DWORD Zero = 0; }
DWORD x = QQ.Zero;

and

#define QQZero 0;
DWORD x = QQZero;

except that the latter is simpler and logically transparent. Similarly, the static methods of a class only provide a roundabout definition of standalone functions that might as well bear no reference to any class at all. The only justification one could fancy is name scope separation; but one does not need classes for that purpose, mere namespaces are enough. Namespaces represent compilation contexts and hence they have nothing to do with object types as represented by classes. Being a kind of preprocessor instructions, namespaces are akin to interfaces, templates and other language constructions used to circumvent the inherent rigidity of the class. In real programming, a good naming convention is much more efficient and flexible than any linguistic exercises.

One could object that putting static objects in a class would also be useful to impose various access restrictions. Personally, I have never seen a practical situation where such formal restrictions would be really needed; in most cases, they only obscure the interdependencies and become a headache for the next programmer, who will need to change the scopes and visibility according to the new business requirements. Once again, a corporate standard is much more efficient, absolutely flexible and logically transparent.

In many cases, when the static properties and methods are essentially unique within the current project, even the namespace interpretation does not justify them. This is especially so with the main() method in Java and C#, which has nothing to do with the object behavior, rather referring to the operating environment, the way the objects are created. In the class terms, one could consider the class Task as describing the possible computation units, and derive a child class MyTask from Task, somehow defining its specific constructor, the equivalent of main(). Then an instance of a class TaskManager (started by an instance of the class OperationSystem, started by an instance of the class BIOS, started by an instance of a class Computer, started by an instance of the class PowerButtonPresser etc.) executes its method StartTask(Task* pointer_to_task_definition) taking class definition reference as a parameter, which contains a statement of the type

Task instance_of_mytask = pointer_to_task_definition –> new();

This illustrates the essential difference of programmatic materialism from object-oriented idealism, that is, the treatment of a class definition as an object rather than a compilation time artifact. The particular implementations of such objects can vary, from pure interpretation (as with JavaScript) to DLL loading (as in ActiveX technology), with the intermediate cases like JVM interpreting some half-compiled code. Still, one can easily guess what is common to all such implementations: a protocol. Protocols are indeed those conventions that allow processing of all the compliant classes, and hence they could be called classes (or rather categories) of class definition. In particular, a protocol specifies how an instance of a class is created using the class definition. Thus, one might demand that each class definition (as an object) had a standard entry point corresponding to the new() method. Alternatively, this method could be dynamically requested from the definition. Of course, any other kinds of protocols are as possible.

In a way, a protocol can be identified with a virtual machine that can be launched in any environment to process the corresponding classes. If there is no appropriate virtual machine, the computation environment cannot initiate the tasks of that type; it will return some error code meaning UNKNOWN_PROTOCOL.

Returning to the static members (properties or member functions) of a class, we observe, that they constitute a part of specific task definition, instantiating the standard method new() to create the operation environment. There is absolutely no need to include them in the definition of the invoked class. The main() function is thus understood as an implementation of a certain virtual machine (a protocol of) processing all the other classes involved. In this picture, any computation is a hierarchy of protocols implemented in virtual machines of all the levels, and one can step beyond mere hierarchical structures, considering their conversion and development. But this will make another story.


[Computers] [Science] [Unism]