Treading Inheritance’s Path – Simplicity vs Basetype/Subtype Issues
It shouldn’t be breaking news that the first code reuse approach I plan to put in the spotlight first is the most overused and misused of all, Inheritance. But I don’t want to be a sadist and bore you to tears (and myself, of course) explaining how to get a nice Dog subclass up and running by inheriting from Animal (ouch!). Rather, I’ll be a little more realistic and we’ll sink our teeth into a naïve, yet functional, PDO adapter. Here we go:<?php
namespace LibraryDatabase;
interface DatabaseAdapterInterface
{
public function executeQuery($sql, array $parameters = array());
}
<?php
namespace LibraryDatabase;
class PdoAdapter extends PDO implements DatabaseAdapterInterface
{
protected $statement;
public function __construct($dsn, $username = null, $password = null, array $options = array())
{
// fail early if the PDO extension is not loaded
if (!extension_loaded("pdo")) {
throw new InvalidArgumentException(
"This adapter needs the PDO extension to be loaded.");
}
// check if a valid DSN has been passed in
if (!is_string($dsn) || empty($dsn)) {
throw new InvalidArgumentException(
"The DSN must be a non-empty string.");
}
// try to create a valid PDO object
try {
parent::__construct($dsn, $username, $password,
$options);
$this->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::ATTR_EMULATE_PREPARES,
false);
}
catch (PDOException $e) {
throw new RunTimeException($e->getMessage());
}
}
public function executeQuery($sql, array $parameters = array())
{
try {
$this->statement = $this->prepare($sql);
$this->statement->execute($parameters);
return $this->statement->fetchAll(PDO::FETCH_CLASS,
"stdClass");
}
catch (PDOException $e) {
throw new RunTimeException($e->getMessage());
}
}
}
When it comes to creating a quick and dirty adapter that just adds some extra functionality to the one provided by the PDO
class, Inheritance is indeed a pretty tempting path. In this case, the adapter performs some basic validation on the inputted database arguments and its executeQuery()
method allows us to run parameterized queries via an easily consumable API. The following snippet demonstrates how to drop the adapter into client code to pull in a few users from the database:
<?php
use LibraryLoaderAutoloader,
LibraryDatabasePdoAdapter;
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();
$adapter = new PdoAdapter("mysql:dbname=mydatabase",
"myfancyusername", "myhardtoguesspassword");
$guests = $adapter->executeQuery("SELECT * FROM users WHERE role = :role", array(":role" => "Guest"));
foreach($guests as $guest) {
echo $guest->name . " " . $guest->email . "<br>";
}
There are no smelly signs of any “incidental” breakages of the LSP, meaning that any instance of PdoAdapter
could be safely swapped out at runtime by a base PDO
object without complaints from the client code, and the adapter has been blessed with all the legacy functionality encapsulated by its parent. Can we dare to ask for a better deal?
Admittedly, there’s a catch. While not explicit, PdoAdapter
is actually exposing to the outside world the whole verbose PDO API, plus the one implemented on it’s own. There might be occasions where we want to avoid this, even if the “IS-A” relation between PdoAdapter
and PDO
is neatly maintained.
We could appeal to the wisdom of the mantra “favor Composition over Inheritance” in such situations and effectively shift into the seas of Composition. In doing so, we would be effectively reusing PDO
’s implementation, but without having to deal with its entire API. Better yet, since there wouldn’t be a “IS-A” relationship, but instead a “HAS-A” one between the corresponding adapter and the PDO
class, we’d be also satisfying our purist OOP instincts. Encapsulation would keep its pristine shell intact.
Of course, one nice way to understand the inner workings of this approach is by setting up a concrete example. Let’s tweak the previous PdoAdapter
class to honor the commandments of Composition.
Composition over Inheritance – of Adapters, Adaptees, and Other Funny Facts
Contrary to popular opinion, it’s ridiculously easy to implement a PDO adapter that rests on the pillars of Composition. Furthermore, the whole refactoring would be limited to just creating a simple wrapper which would inject a nativePDO
object into the constructor. Once again, a hands-on example is the best teacher when it comes to understanding the driving logic of the process:
<?php
namespace LibraryDatabase;
interface DatabaseAdapterInterface
{
public function executeQuery($sql, array $parameters = array());
}
<?php
namespace LibraryDatabase;
class PdoAdapter implements DatabaseAdapterInterface
{
protected $pdo;
protected $statement;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pdo->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,
false);
}
public function executeQuery($sql, array $parameters = array())
{
try {
$this->statement = $this->pdo->prepare($sql);
$this->statement->execute($parameters);
return $this->statement->fetchAll(PDO::FETCH_CLASS,
"stdClass");
}
catch (PDOException $e) {
throw new RunTimeException($e->getMessage());
}
}
}
While the refactored version of the adapter follows in general the formal definition of the Adapter pattern, the adaptee passed along into the adapter’s internals is, in this case, a concrete implementation rather than an abstraction. Considering that we’re attempting to design a slimmed down API for a PDO
object, it doesn’t hurt to be pragmatic and inject the object directly in question without having to go through the hassle of defining an extra interface. If you feel a little more comfortable picking up the interface-based approach, just go ahead and stick to it all the way.
Leaving out of the picture those subtle implementation details, by far the most relevant thing to point out is the PdoAdapter
class is now is a less verbose creature. Using Composition, it hides the whole PDO API from the outside world by exposing only its executeQuery()
method to client code. Although naïve, the example raises a few points worth noting. First, the burden of dealing with potentially dangerous class hierarchies, where subtypes might behave wildly different from their base types blowing away application flow, has quietly vanished in thin air. Second, not only now is the adapter’s API less bloated, it declares an explicit dependency on a PDO
implementation, which makes it easier to see from the outside the collaborator it needs to do its thing.
Yet this slew of benefits come with a not-so-hidden cost: in more realistic situations, it might be harder to create “composed” adapters than it is to create “inherited” ones. The correct path to tread will vary from case to case; be rational and choose the one that fits your requirements the best.
If you’re wondering how to get things rolling with the refactored PdoAdapter
class, the following example should be instructive:
<?php
$pdo = new PDO("mysql:dbname=mydatabase",
"myfancyusername", "myhardtoguesspassword");
$adapter = new PdoAdapter($pdo);
$guests = $adapter->executeQuery("SELECT * FROM users WHERE role = :role", array(":role" => "Guest"));
foreach($guests as $guest) {
echo $guest->name . " " . $guest->email . "<br>";
}
By now we’ve seen at a very broad level two common approaches that can be employed for reusing implementation. So, what comes next? Well, I promised to dig deeper into the niceties of Delegation, too. While admittedly Composition is an implicit form of Delegation, in the earlier example the delegation link was set by declaring a dependency on a PDO implementation in the constructor.
It’s feasible, however, to get a decent delegation mechanism up and running by using one or multiple factory methods, even though this misses much of the advantages of decoupling interface from implementation. Regardless, it’s worth looking at this approach as it comes in helpful for building PDO adapters capable of lazy connections to the database.
Deferring Database Trips through Delegation
Can anything be more delightful than delaying an expensive database trip to the last minute? Avoiding it completely would certainly be better, which is the prime reason why cache system live and breath. But unfortunately, we just can’t have it all sometimes and we need to settle ourselves by designing PDO adapters that encapsulate this nifty ability inside the boundaries of a factory method. Delegation is a simple yet powerful pattern that allows to implement this feature without much hassle. If we would turn our attention to the previousPdoAdapter
class and make it exploit the benefits of Delegation, it would look as follows:
<?php
namespace LibraryDatabase;
interface DatabaseAdapterInterface
{
public function connect();
public function disconnect();
public function executeQuery($sql, array $parameters = array());
}
<?php
namespace LibraryDatabase;
class PdoAdapter implements DatabaseAdapterInterface
{
protected $config = array();
protected $connection;
protected $statement;
public function __construct($dsn, $username = null, $password = null, array $options = array())
{
// fail early if the PDO extension is not loaded
if (!extension_loaded("pdo")) {
throw new InvalidArgumentException(
"This adapter needs the PDO extension to be loaded.");
}
// check if a valid DSN has been passed in
if (!is_string($dsn) || empty($dsn)) {
throw new InvalidArgumentException(
"The DSN must be a non-empty string.");
}
$this->config = compact("dsn", "username",
"password", "options");
}
public function connect()
{
if ($this->connection) {
return;
}
try {
$this->connection = new PDO(
$this->config["dsn"],
$this->config["username"],
$this->config["password"],
$this->config["options"]
);
$this->connection->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$this->connection->setAttribute(
PDO::ATTR_EMULATE_PREPARES, false);
}
catch (PDOException $e) {
throw new RunTimeException($e->getMessage());
}
}
public function disconnect()
{
$this->connection = null;
}
public function executeQuery($sql, array $parameters = array())
{
$this->connect();
try {
$this->statement = $this->connection->prepare($sql);
$this->statement->execute($parameters);
return $this->statement->fetchAll(PDO::FETCH_CLASS,
"stdClass");
}
catch (PDOException $e) {
throw new RunTimeException($e->getMessage());
}
}
}
If you’ve ever had the chance to craft your own PDO masterpiece from scratch, or if you’ve just reused one of the heap available in the wild, the underlying logic of the above adapter should be pretty easy to grasp. Unquestionably, its most engaging facet is the implementation of the connect()
method, as its responsibility is to create a PDO
object on request which is then used in turn to dispatch queries against the database.
While admittedly this is pretty dull and boring, it’s helpful for showing how to use a sort of “hard-coded” Delegation in the implementation of adapters which are clever enough for triggering lazy connections to the database. Furthermore, the example below demonstrates how to put the adapter to use:
<?php
$adapter = new PdoAdapter("mysql:dbname=mydatabase",
"myfancyusername", "myhardtoguespassword");
$guests = $adapter->executeQuery("SELECT * FROM users WHERE role = :role", array(":role" => "Guest"));
foreach($guests as $guest) {
echo $guest->name . " " . $guest->email . "<br>";
}
Call me an unbearable pedantic if you want to, but consuming the adapter is actually a simple process, especially when analyzed from the client code’s standpoint. The major pitfall is that the adapter hides a dependency. This introduces a nasty coupling, hence making the whole implementation a little bit dirty. On the flip side, the adapter’s most appealing virtue rests with its capability for calling the database only when the situation warrants. Naturally, this sort of trade off can be worked out pretty easily by injecting a factory in the adapter, which would be charged with creating a PDO
object when needed.
Even though this approach effectively is the best of both worlds, since it actively promotes the use of Dependency Injection while still employing Delegation, in my view it is somewhat overkill, at least in this case in particular.
Closing Thoughts
I have to admit that it’s rather challenging to come to a judgement on the flaws and niceties exposed by Inheritance, Composition and Delegation without falling into a shallow, shy analysis. Even so, taking the plunge is definitively worthwhile considering each approach is a central pillar of the OOP paradigm. Moreover, while in this case I intentionally did an isolated “per approach” evaluation, something that hopefully was instructive in the end, it’s possible to wire up all the approaches together to implement more efficient code reuse strategies. There’s no mutual exclusion here. Needless to say, it doesn’t make much sense to pile them up blindly just because you can. In all cases, be conscious and make sure to narrow their usage to the context you’re dealing with. Image via FotoliaFrequently Asked Questions (FAQs) about Inheritance, Composition, and Delegation
What is the main difference between inheritance and composition?
Inheritance and composition are both object-oriented programming concepts. Inheritance allows a class to inherit properties and methods from another class, creating a parent-child relationship. This is useful when you want to create a specialized version of an existing class. On the other hand, composition involves creating complex objects by combining simpler ones. It’s a “has-a” relationship, where one object uses another to perform its functions. Composition provides greater flexibility and is generally preferred for code reuse and maintenance.
How does delegation work in object-oriented programming?
Delegation is a method of code reuse where an object passes off some of its responsibilities to another object. It’s a way of making composition more powerful. Instead of doing the work itself, the delegating object keeps a reference to the delegate and forwards requests to it. This allows for flexibility in behavior without subclassing.
Why is composition often preferred over inheritance?
Composition is often preferred over inheritance because it provides greater flexibility. With inheritance, a subclass is tightly coupled to its superclass. This means that changes in the superclass can affect the subclass. Composition, on the other hand, allows you to change the behavior of an object at runtime by changing the objects it’s composed of. It also promotes code reuse and can make your code easier to test and maintain.
Can you use inheritance and composition together?
Yes, inheritance and composition can be used together in a program. In fact, using them together can often lead to more flexible and maintainable code. You can use inheritance to define common behaviors and attributes for a group of related classes, and then use composition to combine these classes in various ways to create more complex behaviors.
What is the “diamond problem” in inheritance?
The diamond problem is a common issue that arises in languages that support multiple inheritance, a feature that allows a class to inherit from more than one superclass. The problem occurs when a class inherits from two classes that have a common superclass. This can lead to ambiguity about which superclass method the subclass should inherit if both superclasses override the method.
How can composition solve the “diamond problem”?
Composition can solve the diamond problem by eliminating the need for multiple inheritance. Instead of inheriting from two classes that have a common superclass, you can create a class that contains instances of these classes. This way, there’s no ambiguity about which method to use, as each composed object has its own copy of the method.
What is the Liskov Substitution Principle and how does it relate to inheritance?
The Liskov Substitution Principle (LSP) is a principle of object-oriented design 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. This principle is closely related to inheritance, as it guides the design of the inheritance hierarchy and helps prevent issues that can arise from improper subclassing.
How does delegation compare to inheritance and composition?
Delegation is a method of code reuse that can be seen as an alternative to both inheritance and composition. Like inheritance, it allows an object to use the behavior of another object. But unlike inheritance, it doesn’t create a parent-child relationship between the objects. Like composition, delegation involves using other objects to perform tasks. But unlike composition, the delegating object doesn’t contain the delegate; it simply keeps a reference to it.
Can you give an example of when to use inheritance, composition, and delegation?
Inheritance is best used when there’s a clear “is-a” relationship between classes. For example, a “Car” class could inherit from a “Vehicle” class. Composition is useful when one object needs to use the functionality of another, but there’s no “is-a” relationship. For example, a “Car” class could be composed of “Engine” and “Wheel” objects. Delegation is useful when an object needs to use the functionality of another, but doesn’t want to expose all of its methods. For example, a “Car” class could delegate some of its methods to an “Engine” object.
What are some best practices for using inheritance, composition, and delegation?
Some best practices include: use inheritance sparingly and only when there’s a clear “is-a” relationship; prefer composition over inheritance for code reuse; use delegation to reuse code without exposing all methods; follow the Liskov Substitution Principle when designing subclasses; and always strive for loose coupling and high cohesion in your code.
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.