4 Refining the object model

The object model presented in Chapter 3 successfully realizes classes, interfaces, encapsulation, polymorphism and single interface inheritance. However, classes may only directly implement one single interface. This is limiting from a modeling perspective—an object should be able to signal that it is both “observable” and “printable,” for instance. If a class is allowed to directly implement multiple interfaces, though, there must be a way for clients to query an object as to whether it supports a given interface, and if so, for a reference through this interface (known as interface navigation).

The Node interface in Chapter 3 stipulates that explicit memory management should be used to manage the lifetime of instances implementing this interface and its descendants. This strategy is inadequate in a setting involving many independent parties. If a reference to an object implementing the Node interface is passed to an external party, there is no way of knowing if this party wishes to hold onto the reference past the lifetime of the call (it could store it as part of its instance data). As a result, it is impossible to know when it is safe to destroy the object. Reference counting can be used to solve this problem, as described in section 2.2.5.1

This chapter aims to rectify these deficiencies, by introducing support for interface navigation and reference counting to the DefaultBinaryOperatorNode class presented in Chapter 3. To this end, a root interface, Fundamental, is introduced in this chapter that all interfaces must extend.

Using the DefaultBinaryOperatorNode class through the BinaryOperatorNode interface requires compile-time knowledge of this interface. The validity of such invocations are checked against the function prototypes contained in the dispatch table of BinaryOperatorNode. As noted in section 2.2.4, not all clients that wish to communicate with an object necessarily have compile-time knowledge of the interfaces it implements, notably scripts interpreted or compiled at runtime, which may not even have been written at the time the script host was compiled. Very late binding entails making it possible to check the validity of operation invocations at runtime, by making the metadata previously only available at compile-time also available at runtime, and thus deploying it to end-users’ systems (this data is sometimes known as runtime type information). This chapter adds support for very late binding to the DefaultBinaryOperatorNode class, by having it implement a new interface, Scriptable, which provides an operation that facilitates very late binding.

Footnotes

  1. The problem with cyclic references would be provoked by adding a parent node reference to every node.