Tony Marston's Blog About software development, PHP and OOP

Composition is a Procedural Technique for Code Reuse

Posted on 24th June 2024 by Tony Marston
Introduction
Inheritance is not procedural
Sharing using procedure calls
Sharing using inheritance
Conclusion
References
Comments

Introduction

This article is in response to Inheritance Is a Procedural Technique for Code Reuse by Yegor Bugayenko.

After 20 years of developing enterprise applications in a variety of other languages I switched to using PHP with its object oriented capabilities in 2002. In order to do this I had to make myself familiar with the 3 pillars of OOP, namely Encapsulation, Inheritance and Polymorphism. I made use of these features in the following way:

The end result of my move to an OO language was that my development times were reduced by a huge amount, thus increasing my levels of productivity, so I judged my efforts to be a success. Imagine my surprise when later on I was castigated by other developers for using inheritance instead of object composition. I was told that I was violating the Composite Reuse Principle (CRP) which said that I should favour composition over inheritance. I tried to find out why inheritance is bad when it is supposed to be one of the 3 pillars of OOP, and eventually I discovered that all the complaints had a common source - they were all using inheritance incorrectly. Their mistake was to inherit from one concrete class to create another concrete class when the correct method was to only ever inherit from an abstract class, as explained in Inheritance is NOT evil.

There was one complaint which caused me to question the sanity of the author, and that was Inheritance Is a Procedural Technique for Code Reuse. I have already countered his arguments in another post, but in this article I intend to show why I believe that it is composition, and not inheritance, which is the inferior technique.


Inheritance is not procedural

I worked with COBOL, the most popular procedural language of all time, for 16 years, and I can say quite categorically, without fear of contradiction, that COBOL never had anything remotely resembling inheritance. It did not have classes, it did not have inheritance, and it did not have polymorphism. While it did have the ability to create procedures in the form of subprograms or subroutines, each with its own PROCEDURE DIVISION and DATA DIVISION, and each with its own unique name, once the procedure call had ended all memory consumed by that procedure, and therefore its state, was lost. This made it impossible to call the same procedure again with a different function to either read or modify its state.

OO capabilities were added to COBOL in 2002, but while I used it it was 100% procedural and it had no support for classes, objects, inheritance or polymorphism.


Sharing using procedure calls

In the following example I have several methods in the default_table class which I want to reuse in the product class.

class default_table {
    public function insertRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function updateRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function deleteRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function getData($where) {
        // ...
        return $fieldarray;
    }
}

class product {
    public function __construct (
        $this->default_table = new default_table;
    }
    public function insertRecord($fieldarray) {
        $fieldarray = $this->default_table->insertRecord($fieldarray);
        return $fieldarray;
    }
    public function updateRecord($fieldarray) {
        $fieldarray = $this->default_table->updateRecord($fieldarray);
        return $fieldarray;
    }
    public function deleteRecord($fieldarray) {
        $fieldarray = $this->default_table->deleteRecord($fieldarray);
        return $fieldarray;
    }
    public function getData($where) {
        $fieldarray = $this->default_table->getData($where);
        return $fieldarray;
    }
}

As you can see this still requires a great deal of code in the product class as each method in the default_table class requires a corresponding method in the product class in order to call it.


Sharing using inheritance

Now look at how much code we can remove yet still produce exactly the same effect. Note the use of the keywords abstract and extends:

abstract class default_table {
    public function insertRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function updateRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function deleteRecord($fieldarray) {
        // ...
        return $fieldarray;
    }
    public function getData($where) {
        // ...
        return $fieldarray;
    }
}

class product extends default_table{
    public function __construct (
        // ...
    }
}

This approach works because the abstract class does not contain any methods that cannot be shared by any subclass. There is nothing "concrete" in this abstract class which would have any effect on any other concrete subclass. Each table's metadata is maintained in a separate table structure file which is loaded into the object when it is instantiated. The only methods that I need to add to each concrete class are the "hook" methods which are defined in the abstract class


Conclusion

It should be obvious in the previous code samples that there are two ways of executing a shared procedure:

  1. Include a call to the shared procedure in your own code. This may also require instantiating the object which contains this procedure.
  2. Inherit the procedure from an abstract superclass. This does not require the insertion of any additional code to call the shared procedure. The only reference to the superclass is with the "extends" keyword.

Option #2 allows the use of the Template Method Pattern. According to the Gang of Four (GoF) this pattern is a fundamental technique for code reuse as it implements the Hollywood Principle (Don't call us, we'll call you). This is also known as Inversion of Control (IoC) and is at the heart of framework design.

Option #1 is far less useful as it cannot implement the Hollywood Principle.

Option #2 is not available in procedural languages.

This should then dispell the myth that "inheritance is a procedural technique for code reuse" when in fact it is composition which is the procedural and - dare I say it - the most inferior technique.

There are far too many naive programmers who have been taught inheritance is bad, use composition instead but don't know why it is supposed to be bad, so they invent one misguided excuse after another. An experienced programmer should realise that it is not inheritance itself which is bad, it is only the incorrect use of inheritance (i.e. inheriting from a concrete class) which causes problems. The "correct" use of inheritance (i.e. only inheriting from an abstract class) is discussed in the following:

Here endeth the lesson. Don't applaud, just throw money.


References

These are reasons why I consider some ideas on how to do OOP "properly" to be complete rubbish:


counter