Pitfalls with self-replicating objects
When designing a class, it's a good thing to consider reusability. The subject of this entry is to examine objects of a class that are able to replicate, and the consequences this has on making the class reusable.
Object replication means creating a new object from within the code. It doesn't have to be a copy, it just means here that a new object of the same class is created. There is no special consideration regarding the state of the new object.
Let's take a look at some sample code:
The effect of calling
mitosis is to create a new cell identical to the original.
All is good so far, however things become messy if we want to write a new class inheriting from BASIC_CELL. You noticed the create intruction, it calls
make and therefore
make must be available for creation for all descendants.
This can be a problem, for instance if the descendant has no obvious default state. It's also a problem if we don't want the outside world to create objects of the descendant class with
make. Fortunately, a work around this is to restrict the export state of
make, as the code below demonstrates.
This code fails to compile if void safety is turned on, and types are attached by default. In that case, there is no way for
make to find the correct value of
Theoretically, a deferred class can inherit from a parent without trouble. But no deferred class can inherit from BASIC_CELL, because it would have to list
make as a creation procedure, and deferred classes cannot have them.
Fixing the code
A good workaround to this problem is to have a feature dedicated to the creation of the new object. The idea is that it can be undefined and redefined based on the descendants needs. Undefined in deferred classes, redefined to use the appropriate creation procedure otherwise.
For BASIC_CELL, the code becomes:
Here is an example of a deferred class with the undefine clause. Redefining the
create_new feature is left to next descendants in the hierarchy.
As we have seen, using create to duplicate an object makes a class harder to reuse, unless the use of create is restricted to be in a dedicated feature. Descendants can then undefine or redefine that feature in a way that suits their needs.
The compiler could detect this situation and issue a warning if the feature where create is found to contain other instructions.
Last but not least, perhaps someone could take a look at classes from the standard libraries, since at least TWO_WAY_LIST can replicate. I have myself written code that inherits from TWO_WAY_LIST, and working around this issue has annoyed me quite a bit, prompting this post.