Constructors and Breaking the Liskov Substitution Principle

Share this article

Key Takeaways

  • Object construction is not part of the contract honored by its implementers. A constructor inside an interface is pointless as it doesn’t contribute to defining the behavior of the concrete implementations.
  • Implementing different constructors down the same hierarchy doesn’t violate the Liskov Substitution Principle (LSP). The LSP is not infringed as long as the contract exposed by the two classes is properly honored.
  • Having a hierarchy of classes where the subtypes are constructed differently than the base types does not break the desired substitutability promoted by the LSP.
  • The LSP is not violated even when the constructor’s signature and implementation accommodate an array of connection parameters instead of digesting plain scalars. Client code remains agnostic to this change as long as the contract it consumes remains the same.
  • While it’s important to adhere to programming principles, providing them with a dogmatic connotation which must be blindly fulfilled can be harmful. Being aware of the consequences of “construction-restrictive” contracts can help mitigate the proliferation of inflexible practices.
At the risk of being targeted by the PHP hate-mongers, I must confess that I’m pretty comfortable with PHP’s object model. I’m not that naïve to claim it’s a full-blown model exposing all the bells and whistles of other “fatter” players, such as C++ and Java, but despite of all its quirks, PHP delivers what it promises at face value. I would say however that the model is notoriously permissive considering the relaxed constraints it imposes when it comes to defining userland interfaces. One issue with the model’s “forgiveness” is that one fine day you may wake up in a whimsical mood and specify a constructor inside an interface just because you can, without a twinge of guilt. If you think this through, the existence of a constructor as part of an interface is pretty pointless. First off, every time you define a contract, you’re actually abstracting away the behavior of the concrete implementations and leaving out of the picture how they must be assembled down the road. Second, how in the world you can get client code consuming a constructor once the implementers have been instantiated? There’s no parallel universe in which you can do such things unless without sloppily polluting consumers with a bunch of new operators, which should live and breathe somewhere else according to the “isolate application logic from object construction” mantra. Rants aside, the morale of the story can be boiled down to the following: “Object construction is not part of the contract honored by its implementers”. It’s easier to grasp concepts by example rather than reading dull theory, so in this article I’ll be demonstrating from a practical standpoint how the implementation of different constructors down the same hierarchy isn’t a violation of the Liskov Substitution Principle, which is a reason why you shouldn’t fall into the temptation of tainting your interfaces with constructors.

The Myth of LSP Breakage

I guess I’ve already made a valid point with regard to avoiding constructors in interfaces, a process that certainly helps to generate contracts that describe and prescribe the behavior of certain object types (a.k.a. Subtype Polymorphism) but not how to create the objects in question. It might be incidentally harder, however, to understand why overriding a parent constructor in a subclass, or eventually implementing a brand new one doesn’t violates the LSP. Let’s say we need to create a PDO adapter just by hanging a lightweight subclass from the native PDO. The adapter’s contract, along with the corresponding implementation, could look as follows:
<?php
namespace LibraryDatabase;

interface DatabaseAdapterInterface
{     
    public function executeQuery($sql, array $parameters = array());
    public function select($table, array $bind, $operator = "AND");
    public function insert($table, array $bind);
    public function update($table, array $bind, $where = "");
    public function delete($table, $where = "");
}
<?php
namespace LibraryDatabase;

class PdoAdapter extends PDO implements DatabaseAdapterInterface
{
    private $statement;   
    
    public function __construct($dsn, $username = null, $password = null, array $options = array()) {
        if (!extension_loaded("pdo")) {
            throw new InvalidArgumentException(
                "The adapter needs the PDO extension to be loaded.");
        }
        if (!is_string($dsn) || empty($dsn)) {
            throw new InvalidArgumentException("The DSN is invalid.");
        }
        parent::__construct($dsn, $username, $password, $options);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }
    
    // Prepare and execute an SQL statement
    public function executeQuery($sql, array $parameters = array()) {
        try {
            $this->statement = $this->prepare($sql);
            $this->statement->execute($parameters);
            $this->statement->setFetchMode(PDO::FETCH_OBJ);
            return $this->statement;
        }
        catch (PDOException $e) {
            throw new RunTimeException($e->getMessage());
        }
    }
    
    // Prepare and execute a SELECT statement
    public function select($table, array $bind = array(), $operator = "AND") {
        if ($bind) {
            $where = array();
            foreach ($bind as $col => $value) {
                unset($bind[$col]);
                $bind[":" . $col] = $value;
                $where[] = $col . " = :" . $col;
            }
        }
        $sql = "SELECT * FROM " . $table 
             . (($bind) ? " WHERE " 
             . implode(" " . $operator . " ", $where) : " ");
        return $this->executeQuery($sql, $bind);
    }
    
    // Prepare and execute an INSERT statement
    public function insert($table, array $bind) {
        $cols = implode(", ", array_keys($bind));
        $values = implode(", :", array_keys($bind));
        foreach ($bind as $col => $value) {
            unset($bind[$col]);
            $bind[":" . $col] = $value;
        }
        $sql = "INSERT INTO " . $table 
             . " (" . $cols . ")  VALUES (:" . $values . ")";
        $this->executeQuery($sql, $bind);
        return $this->lastInsertId();
    }
    
    // Prepare and execute an UPDATE statement
    public function update($table, array $bind, $where = "") {
        $set = array();
        foreach ($bind as $col => $value) {
            unset($bind[$col]);
            $bind[":" . $col] = $value;
            $set[] = $col . " = :" . $col;
        }
        $sql = "UPDATE " . $table . " SET " . implode(", ", $set) 
             . (($where) ? " WHERE " . $where : " ");
        return $this->executeQuery($sql, $bind)->rowCount();
    }
    
    // Prepare and execute a DELETE statement
    public function delete($table, $where = "") {
        $sql = "DELETE FROM " . $table 
             . (($where) ? " WHERE " . $where : " ");
        return $this->executeQuery($sql)->rowCount();
    }
}
The batch of tasks performed by the PdoAdapter class are indeed straightforward, limited to running a few prepared queries via its executeQuery()
method and shooting some CRUD operations against a given table. The most relevant detail to highlight here is how the adapter overrides its parent’s constructor in order to do some checks and see if the PDO extension has been installed before starting consuming it. Here’s how we’d get the adapter up and running:
<?php
use LibraryLoaderAutoloader,
    LibraryDatabasePdoAdapter;
  
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();
    
$adapter = new PdoAdapter("mysql:dbname=test", "myusername", "mypassword");
   
$stmt = $adapter->prepare("SELECT * FROM users");

$stmt->execute();

$users = $stmt->fetchAll(PDO::FETCH_OBJ);

foreach ($users as $user) {
    echo $user->name . " " . $user->email . "<br>";
}
I’m intentionally avoiding the adapter’s executeQuery() method and instead appealing to the native execute() method to pull in a few users from the database. At first blush this all seems to be pretty boring boilerplate stuff that should be skipped over for more interesting things… but bear with me because indeed there’s a tiny catch here worth stressing. Even considering that the adapter effectively implements a different constructor from the default one provided by PDO, the contract it agrees with client code is neatly maintained from top to bottom. In short, this means that having disparate constructor implementations in a base type and in a subtype by no means infringes the LSP substitutability rules as long as the contract exposed by the two classes is properly honored. Logically, this can be seen more clearly if we switch the previous script over to a native PDO instance rather than consuming the adapter:
<?php
$adapter = new PDO("mysql:dbname=test", "myusername", "mypassword");

$stmt = $adapter->prepare("SELECT * FROM users");

$stmt->execute();

$users = $stmt->fetchAll(PDO::FETCH_OBJ);

foreach ($users as $user) {
    echo $user->name . " " . $user->email . "<br>";
}
The fact of having a hierarchy of classes, even a very elemental one, where the subtypes are constructed differently than the base types has literally nothing to do with breaking the desired substitutability that’s actively promoted by the LSP. You might argue that the signatures of the adapter and the native PDO look pretty much the same, and that’s why substitutability is in this case preserved so well. But it’s really easy to refute an argument like this. For instance, I could take a more pragmatic approach and refactor the adapter’s constructor so that it can accept an array of named connection arguments:
<?php
namespace LibraryDatabase;

class PdoAdapter extends PDO implements DatabaseAdapterInterface
{
    private $statement;   

    public function __construct(array $config = array()) {
        if (!extension_loaded("pdo")) {
            throw new InvalidArgumentException(
                "The adapter needs the PDO extension to be loaded.");
        }
        if (!isset($config["dsn"]) || !is_string($config["dsn"])) {
             throw new InvalidArgumentException(
                "The DSN has not been specified or is invalid.");
        }
        $username = $password = $options = null;
        extract($config);
        parent::__construct($dsn, $username, $password, $options);
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }
    
    // the same implementation goes here
}
At this point the adapter’s constructor is a pretty different creature whose signature and implementation neatly accommodate an array of connection parameters instead of digesting plain scalars. With such a drastic refactoring in place, one might take for granted that isn’t possible to swap out at runtime an instance of the adapter with a native PDO one anymore. Rush judgments tend to be dangerous, something that the following snippet puts in evidence:
<?php
$adapter = new PdoAdapter(array(
    "dsn"      => "mysql:dbname=test", 
    "username" => "myusername", 
    "password" => "mypassword"
));

$stmt = $adapter->prepare("SELECT * FROM users");

$stmt->execute();

$users = $stmt->fetchAll(PDO::FETCH_OBJ);

foreach ($users as $user) {
    echo $user->name . " " . $user->email . "<br>";
}
Even though instantiating an instance of the adapter is now quite a different process, the client code remains entirely agnostic about this change in the contract it consumes remains the same! Still, if you feel a pinch of skepticism and think the example falls short when it comes to demonstrating the interchangeability between types of a given hierarchy, look at the following which refactors even more drastically the constructor and supplies the connection arguments via a naive DTO (Data Transfer Object):
<?php
namespace LibraryDatabase;

interface ConnectionDefinitionInterface
{
    public function getDsn();
    public function getUserName();
    public function getPassword();
    public function getOptions();
}
<?php
namespace LibraryDatabase;

class ConnectionDefinition implements ConnectionDefinitionInterface
{
    private $dsn;
    private $username;
    private $password;
    private $options = array();

    public function __construct($dsn, $username = null, $password = null, array $options = array()) {  
        if (!is_string($dsn) || empty($dsn)) {
            throw new InvalidArgumentException("The DSN is invalid.");
        }
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->options = $options;
    }
    
    public function getDsn() {
        return $this->dsn;
    }
    
    public function getUserName() {
        return $this->username;
    }
    
    public function getPassword() {
        return $this->password;
    }
    
    public function getOptions() {
        return $this->options;
    }
}
<?php
namespace LibraryDatabase;

class PdoAdapter extends PDO implements DatabaseAdapterInterface
{
    private $statement;   
    
    public function __construct(ConnectionDefinitionInterface $connectionDefinition) {
        if (!extension_loaded("pdo")) {
            throw new InvalidArgumentException(
                "The adapter needs the PDO extension to be loaded.");
        }
        parent::__construct(
            $connectionDefinition->getDsn(), 
            $connectionDefinition->getUserName(), 
            $connectionDefinition->getPassword(), 
            $connectionDefinition->getOptions()
        );
        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }

    // the same implementation goes here
}
While it’s fair to admit that DTOs don’t have a prolific existence in the PHP world (though some popular frameworks like Zend Framework 2 and Simfony 2 use them ubiquitously), in this case I appealed to a simple one to pass a set of connection arguments to the adapter’s constructor. The radical change, for obvious reasons, is going to ripple artifacts toward the code responsible for instantiating the adapter, be it a DIC, a low-level factory, or any other independent mechanism along that line that doesn’t interfere explicitly with application logic:
<?php
$adapter = new PdoAdapter(
    new ConnectionDefinition("mysql:dbname=test", "myusername", "mypassword")
);
As long as the adapter’s contract remains untouched along the way, it’s possible to interchange an instance of the adapter with a PDO one and client code won’t pitch a single compliant. Though this example might be somewhat overkill when it comes to pushing PDO functionality inside the boundaries of a slim wrapper, it does an excellent job at showing the flagrant LSP breakages that are easily found in the wild occur because of other reasons, which I dare to claim aren’t even related to the “flaws” of the base type/subtype construction schema.

Closing Remarks

The mantra has been said over and over, but it’s worth stressing it once again: providing a certain programming principle or pattern with a dogmatic connotation which must be blindly fulfilled is something almost as harmful as not using those patterns and principles at all, hence letting the chaos of pragmatism dramatically rot the quality of a system. Aside from attempting to demystify constructors as one of the root causes of potential LSP infringements, my argument against including constructors in interfaces certainly isn’t meant to be dogma. But being aware of the consequences of this sort of “construction-restrictive” contracts just because PHP’s object model is relaxed in that aspect might help to mitigate the proliferation of this inflexible, pointless practice. Image via Fotolia

Frequently Asked Questions about Constructors and the Liskov Substitution Principle

What is the Liskov Substitution Principle (LSP)?

The Liskov Substitution Principle (LSP) is a concept in object-oriented programming that states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, the objects of a superclass should be able to be replaced with objects of a subclass without affecting the correctness of the program.

How does the LSP relate to constructors?

Constructors are special methods in a class that are automatically called when an object of that class is created. They usually initialize the data members of the new object. In the context of LSP, constructors should be designed in a way that they don’t violate the LSP. That is, a constructor of a subclass should not do less than the constructor of its superclass.

Can you provide an example of violating the LSP?

Let’s consider a simple example. Suppose we have a superclass Bird and a subclass Penguin. The Bird class has a method fly(). Now, if we create an object of type Bird but it’s actually a Penguin, and we call the fly() method, it will lead to an error or unexpected behavior because penguins can’t fly. This is a violation of the LSP.

How can I avoid violating the LSP?

To avoid violating the LSP, you should ensure that your subclasses are fully substitutable for their base classes. This might involve making sure that they implement all the methods of the base class appropriately. If a subclass can’t support a method of the base class, it might be a sign that your class hierarchy needs rethinking.

What is the importance of the LSP in programming?

The LSP is important because it promotes the interchangeability of components without altering the correctness of the program. It leads to more robust, maintainable, and reusable code. Violations of the LSP can lead to unexpected behavior and bugs.

How does the LSP relate to the SOLID principles?

The LSP is the ‘L’ in the SOLID principles of object-oriented design. These principles are guidelines for writing code that is easy to understand, maintain, and expand. The other principles are Single Responsibility, Open-Closed, Interface Segregation, and Dependency Inversion.

Can you provide an example of adhering to the LSP?

Let’s consider the Bird and Penguin example again. To adhere to the LSP, we could create a new superclass, say Animal, and have Bird and Penguin as subclasses. The fly() method would be in the Bird class but not in the Penguin class. This way, we can substitute a Bird object with a Penguin object without causing any issues.

What happens if I violate the LSP?

Violating the LSP can lead to problems in your code. It can cause bugs that are hard to track down because the program behaves unexpectedly. It can also make your code harder to understand and maintain, as the behavior of the subclasses is not what would be expected from the base class.

How does the LSP apply to other programming languages?

The LSP is a principle of object-oriented design, so it applies to any object-oriented programming language, such as Java, C++, Python, etc. The way you implement it might vary slightly from language to language, but the principle remains the same.

How can I check if my code violates the LSP?

Checking for LSP violations can be done through code reviews and testing. Look for places where a subclass is doing less than its superclass or behaving in a way that’s unexpected for an object of the superclass. Automated tools can also help detect potential LSP violations.

Alejandro GervasioAlejandro Gervasio
View Author

Alejandro Gervasio is a senior System Analyst from Argentina who has been involved in software development since the mid-80's. He has more than 12 years of experience in PHP development, 10 years in Java Programming, Object-Oriented Design, and most of the client-side technologies available out there.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week