Decoupling Interfaces from Implementation – Using Separated Interfaces

Share this article

Key Takeaways

  • Utilizing separated interfaces enhances system cohesion and component decoupling, allowing for flexible runtime component integration through dependency injection.
  • Separated interfaces facilitate the inversion of dependencies between components, supporting a cleaner, more modular architecture that adheres to the Dependency Inversion Principle.
  • Implementing separated interfaces in PHP is straightforward and can significantly improve the maintainability and scalability of applications by defining clear contracts for component interaction.
  • Practical examples, such as decoupling a Domain Model from Data Mapper logic using separated interfaces, illustrate how to maintain system integrity while enabling components to remain agnostic of persistence mechanisms.
  • While beneficial, the use of separated interfaces requires careful consideration and should be employed judiciously to avoid unnecessary complexity in smaller or less complex projects.
If you’ve ever faced the same dilemma, surely you’ll grasp the sense of the following description: Cohesion and Decoupling, two seemingly naïve yet fundamental OOP cornerstones responsible for the incessant drumming that crushes the minds of most software developers, often crossing the boundary of what’s a tangible goal into utopia, away from the pragmatism and reality. I don’t want to sound excessively ominous, but it’s fair to admit that designing cohesive, loosely-coupled systems is anything but a simple experience. In most cases, achieving such pristine features requires a lot of planning at the initial stages and going through the proper refactoring cycles later on as the system evolve and grow over time. Despite this, the OOP paradigm per se provides a set of well-trusted methodologies and principles right out of the box that together facilitate the implementation of cohesive, highly-decoupled components, even if they do have to be eventually refactored during their respective life cycles. Consider, for instance, the use of a few segregated interfaces along with namespaced classes. When put to work in sync, they help wire up together components that are semantically related to each other, therefore increasing the overall level of cohesion exposed by a system. On the flip side, the definition of the interfaces themselves, either in the form of interface constructs or virtual/abstract classes, makes concrete implementations dependent upon abstractions, hence promoting the decoupling of components in such a way that they can be “rewired” differently at runtime, usually by means of Dependency Injection. When analyzed from a bird’s eye view, it becomes clear that the combined forces of good OOP practices and the proper mixture of design patterns are in most cases an effective solution that allows us to achieve a decent level of Cohesion and Decoupling. Still, issues may arise when it’s necessary to “redefine” explicitly the way that one component, or even an entire layer, in a multi-tiered design must interplay with others. In a traditional system design, the dependencies between its decoupled components is defined through the respective interfaces, which are usually placed in the same component housing the concrete implementations. There might be a few particular situations where the dependencies must be reversed, in response to specific requirements. In such a case, the inversion in question can be easily accomplished by dropping the interface into one component and the corresponding implementations into a different one. Considering that the concept may sound rather tangled and twisted at first, this shifting of protocols between components lives and breaths under the umbrella of a basic design pattern known as Separated Interface, which at least to some extent, goes hand in hand with the commandments of the Dependency Inversion Principle. Moreover, because of the pattern’s easy-going nature, it’s feasible to implement it in PHP without much fuss, so in this article I’ll show you how to exploit the benefits that separated interfaces bring to the table in a fairly approachable fashion.

Trespassing in Forbidden Terrain – Calling Data Mapper Logic from a Domain Model

While the logic behind separated interfaces is pretty straightforward to digest, in all cases there must always be a compelling reason that justifies their implementation since the process of inverting the dependencies between components in an application is a far cry from being an arbitrary, whimsical decision. This raises an interesting question as well: why should one invert component dependencies if, after all, in their current state they work just fine? There may be some situations when the inversion can be used as a mechanism for increasing the level of decoupling between the involved components. In simple terms, by putting an interface in one module and the implementation somewhere else means to define explicitly the protocol that should be used in the outside world for accessing the module in question. A good way to see the effects of separated interfaces in action is with some testable examples. So, let’s suppose that we have a simple Domain Model which needs to access some logic in the mapping layer. In a classic implementation this would pretty much be viewed as a cardinal sin since a Domain Model by definition must be unaware of any form of persistence down the line. When using a separated interface, though, we can achieve this and still keep the model completely agnostic. To better understand the inner workings of this process, let’s begin implementing a basic model. Here’s how its first block looks:
<?php
namespace Model;

class AbstractEntity
{
    protected $id;
    
    //set values for protected/private fields via the corresponding mutators
    public function __set($field, $value) {
        $this->checkField($field);
        $mutator = "set" . ucfirst(strtolower($field));
        method_exists($this, $mutator) && 
            is_callable(array($this, $mutator)) 
            ? $this->$mutator($value)
            : $this->$field = $value;
        return $this;                 
    }
      
    // get values from protected/private fields via the corresponding accessors 
    public function __get($field) {
        $this->checkField($field);
        $accessor = "get" . ucfirst(strtolower($field));
        return method_exists($this, $accessor) && 
            is_callable(array($this, $accessor))
            ? $this->$accessor() 
            : $this->$field;
    }
    
    protected function checkField($field) {
        if (!property_exists($this, $field)) {
            throw new InvalidArgumentException(
                "Setting or getting the field '$field'j is not valid for this entity.");
        }
    }
    
    // sanitize strings assigned to the fields of the entity
    protected function sanitizeString($value, $min = 2, $max = null) {
        if (!is_string($value) 
            || strlen($value) < (integer) $min 
            || ($max) ? strlen($value) > (integer) $max : false) {
            throw new InvalidArgumentException(
                "The value of the field accessed must be a valid string.");
        }
        return htmlspecialchars(trim($value), ENT_QUOTES);
    }
    
    // handle IDs
    public function setId($id)
    {
        if ($this->id !== null) {
            throw new BadMethodCallException(
                "The ID for this entity has been set already.");
        }
        if (!is_int($id) || $id < 1) {
            throw new InvalidArgumentException(
              "The ID of this entity is invalid.");
        }
        $this->id = $id;
        return $this;
    }
    
    public function getId() {
        return $this->id;
    }
}
Considering that the goal here is to implement a rather prototypical model whose core functionality can be easily extended at will, the above AbstractEntity
class behaves pretty much like a Layer Supertype which encapsulates logic common to all the domain subclasses that might be incidentally derived further down the road. This includes performing a few basic tasks, such as handling IDs, sanitizing strings, and mapping calls to mutators/accessors through __set() and __get(). With the base class in place, now it’s fairly simple to create additional subclasses that define the data and behavior of concrete domain objects. How about setting a few charged with modeling the classic blog posts/comments relationship?
<?php
namespace Model;

interface PostInterface
{
     public function setId($id);
     public function getId();
     
     public function setTitle($title);
     public function getTitle();
     
     public function setContent($content);
     public function getContent();
     
     public function getComments();
}
<?php
namespace Model;

interface CommentFinderInterface {
    public function findById($id);
    public function findAll(array $conditions = array());
}
<?php
namespace Model;

class Post extends AbstractEntity implements PostInterface
{
    protected $title;
    protected $content;
    protected $comments;
    protected $commentFinder;

    public function __construct($title, $content, CommentFinderInterface $commentFinder)  {
        $this->setTitle($title);
        $this->setContent($content);
        $this->commentFinder = $commentFinder;
    }
    
    public function setTitle($title) {
        $this->title = $this->sanitizeString($title);
        return $this;
    }
    
    public function getTitle() {
        return $this->title;
    }
    
    public function setContent($content) {
        $this->content = $this->sanitizeString($content);
        return $this;
    }
    
    public function getContent() {
        return $this->content;
    }
    
    public function getComments() {
        if ($this->comments === null) {
            $this->comments = $this->commentFinder->findAll(
                array("post_id" => $this->id));
        }
        return $this->comments;
    }
}
While admittedly the logic that drives the Post class is contrived, boiled down to just validating and assigning a few values to the fields of blog post objects, it exposes a detail worth stressing: it gets injected into its constructor an implementation of the previous CommentFinderInterface which is used for pulling in the associated comments for the persistence layer. This shows in a nutshell how to use a separated interface for reversing the dependencies between the model (at least a portion of it) and any other layer which must interact with it. In other words, the model is now a stand-up client module, responsible for defining the abstraction of its own accessing protocol. This simple change allows us to do all sorts of clever things without feeling a pinch of guilt, including ones that in any other context would be considered blasphemous. For example, we could make the model invoke mapping logic and retrieve the comments from the database while still keeping it unaware of the mapper itself. The model would always see the interface, not the implementer, be it a mapper or anything else. Of course, before we see how to get the model calling logic in a data mapper, we should create the subclass charged with modeling the aforementioned comments. Here’s the additional derivative, along with the interface that it implements:
<?php
namespace Model;

interface CommentInterface
{
     public function setId($id);
     public function getId();
     
     public function setContent($content);
     public function getContent();
     
     public function setAuthor($author);
     public function getAuthor();
}
<?php
namespace Model;

class Comment extends AbstractEntity implements CommentInterface
{
    protected $content;
    protected $author;

    public function __construct($content, $author) {
        $this->setContent($content);
        $this->setAuthor($author);
    }
    
    public function setContent($content) {
        $this->content = $this->sanitizeString($content);
        return $this;
    }
    
    public function getContent() {
        return $this->content;
    }
    
    public function setAuthor($author) {
        $this->author = $this->sanitizeString($author);
        return $this;
    }
    
    public function getAuthor() {
        return $this->author;
    }
}
There’s no need to peer too deep into the API of the earlier Comment class in order to grasp its underlying logic, as its responsibility is pretty narrow. It just creates comment objects in a valid state, which are hydrated via a couple of scalar fields such as the comment’s content and the corresponding author.

Pulling in Comments from Storage through a Data Mapper

Now it’s time to show off a little as we’ve managed to implement a functional Domain Model which clings off a separated interface and declares explicitly the contract that should be used for pulling in comments from the persistence layer. While this is all well and good, there’s still a missing piece in this puzzle; to get things finally rolling, we need to define at least one single implementer of this interface so that the comments can be injected into the internals of a few post objects. In this case in particular, the implementer will be a comment mapper as it reflects most of the use cases that might exist in production. Needless to say that it’s possible to switch over any other form of mediator to the persistence layer, such a web service or even an in-memory cache system. To keep the code samples a bit clearer and easier to follow, though, I’ll stick to a basic data mapper implementation. Sitting on a decent data mapper between the Domain Model and the database and being capable of at least of performing CRUD operations on a few domain objects is quite a burdening task, as the mapper not only must dissect the bones of objects themselves in order to persist them, but in turn it has to invert the process and reconstitute them on request. This duality of roles makes data mappers complex to set up and in most cases delegated, in the name of mental sanity, to the duties of a third-party library. Even though, there’s no need to be have such a frantic attitude when it comes to implementing the data mappers since all that we need to do now is create a naïve one that implements the previous CommentFinderInterface
so that the corresponding comments can be dragged out from the storage and injected into the internals of the associated post objects. The below one does exactly that:
<?php
namespace Mapper;
use LibraryDatabaseDatabaseAdapterInterface,
    ModelCommentFinderInterface,
    ModelNullComment,
    ModelComment;   

class CommentMapper implements CommentFinderInterface
{
    protected $adapter;
    protected $entityTable = "comments";
    
    public function __construct(DatabaseAdapterInterface $adapter) {
        $this->adapter = $adapter;
    }
    
    public function findById($id) {
        $this->adapter->select($this->entityTable, 
            array("id" => $id));
        if (!$row = $this->adapter->fetch()) {
            return null;    
        }
        return $this->loadComment($row);
    }
    
    public function findAll(array $conditions = array()) {
        $this->adapter->select($this->entityTable, $conditions);
        $rows = $this->adapter->fetchAll();
        return $this->loadComments($rows);
    }
    
    protected function loadComment(array $row) {
        return new Comment($row["content"], $row["author"]);
    }
    
    protected function loadComments(array $rows) {
        $comments = array();
        foreach ($rows as $row) {
            $comments[] = $this->loadComment($row);
        }
        return $comments;
    }
}
For the sake of brevity, I decided to keep the implementation of CommentMapper a simple wrapper around the findById() and findAll() methods. However, if you feel adventurous and want to add some extra methods that allow it to insert, update, and delete comments from the associated table, go ahead and have some fun leveraging your remarkable coding skills. Naturally, the mapper’s most appealing facet is its expressive, not to say explicit, dependency toward the protocol declared by the model when it comes to retrieving comments from the storage. The actual beauty of this schema is because of the fact that although the dependency is prescriptive, it relies on the abstraction of an interface. This means that neither the model is aware of the mapper nor the mapper has an overall vision of the model. That’s pretty motivational considering that the level of decoupling between the two components have been nicely enhanced by using just a simple abstraction. Of course, all this talk might sound like just a lot of babbling if I didn’t back it up with a concrete example.
<?php
use LibraryLoaderAutoloader,
    LibraryDatabasePdoAdapter,
    MapperCommentMapper,
    ModelPost,
    ModelComment;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$adapter = new PdoAdapter("mysql:dbname=test", "fancyusername",
    "hardtoguesspassword");

$commentMapper = new CommentMapper($adapter);

$post = new Post("A naive sample post", 
    "This is the content of the sample post", 
    $commentMapper);
$post->id = 1;

echo $post->title . " " . $post->content . "<br>";

foreach ($post->comments as $comment) {
    echo $comment->content . " " . $comment->author . "<br>";
}
Is it just me or does passing along the data mapper into a post object look like something that would make me cringe? There’s no reason to panic; both elements have been properly shielded from each other through the contract of a separated interface. Furthermore, since the mapper is merely an element taking advantage of Polymorphism, it’d be really easy to swap out other implementations of the CommentFinderInterface. In either case, the example should be illustrative enough for understanding what’s behind the rather bullying fences of separated interfaces, and how its implementation in a few specific use cases can be pretty useful when it comes to reversing the dependencies between one or more components while keeping a neat level of decoupling throughout the whole system.

Closing Remarks

If you’re like me, probably the first time you heard something about separated interfaces you thought about them pretty much like another twisted, hard-to-understand pattern, with little or no real application in the real world. Despite such a knee-jerk reaction, the logic that drives separated interfaces is a lot easier to digest than one might think. Plus, the slew of benefits that separated interfaces bring to the table are indeed plethoric as they allow you to set up in a straightforward fashion loosely-coupled client modules which define the contracts that should be used by other packages. As with the overwhelming list of design patterns that exist out there, having a blind worshiping attitude about separated interfaces just doesn’t make much sense, hence they should be used with due caution and only if the situation warrants reversing the dependencies between application components. Appeal to your common sense, don’t be whimsy, and use them when you actually think they’re going to have a positive impact in your design. Image via kuppa / Shutterstock

Frequently Asked Questions (FAQs) on Decoupling Interfaces from Implementation

What is the main purpose of decoupling interfaces from implementation in software development?

The primary purpose of decoupling interfaces from implementation is to enhance the modularity of the software, making it more maintainable, scalable, and flexible. By separating the interface from the implementation, developers can change the underlying code without affecting the interface that users interact with. This separation also allows for easier testing and debugging, as individual components can be isolated and tested independently.

How does decoupling interfaces from implementation improve software testing?

Decoupling interfaces from implementation significantly improves software testing by allowing individual components to be tested in isolation. This means that if a bug is found, it can be traced back to a specific component, making it easier to identify and fix. Additionally, decoupling allows for mock testing, where dummy objects are used to simulate the behavior of real objects in controlled ways.

Can you provide an example of decoupling interfaces from implementation?

Sure, let’s consider a simple example of a music player application. The interface might include buttons for play, pause, and stop. The implementation, on the other hand, would be the code that actually plays the music when the play button is pressed. By decoupling the interface from the implementation, the code that plays the music can be changed or updated without affecting the interface that the user interacts with.

What are the potential drawbacks of decoupling interfaces from implementation?

While decoupling interfaces from implementation has many benefits, it can also introduce complexity into the software development process. It requires careful planning and design to ensure that the interface and implementation work seamlessly together. Additionally, it can make the codebase larger and more difficult to manage, particularly for large-scale projects.

How does decoupling interfaces from implementation contribute to software scalability?

Decoupling interfaces from implementation contributes to software scalability by making it easier to add, update, or remove components without affecting the overall system. This means that as the software needs to grow or change, individual components can be modified or replaced without disrupting the user interface or other parts of the system.

Is decoupling interfaces from implementation applicable to all types of software development?

While decoupling interfaces from implementation is a common practice in many types of software development, it may not be suitable for all projects. For small-scale projects or those with a short lifespan, the added complexity of decoupling may not be worth the potential benefits. However, for larger, more complex projects, decoupling can significantly enhance maintainability, scalability, and testability.

How does decoupling interfaces from implementation affect software performance?

Decoupling interfaces from implementation can potentially improve software performance by allowing for more efficient code. By isolating individual components, developers can optimize the performance of each component independently. However, if not done properly, decoupling can also introduce additional overhead and complexity, which could negatively impact performance.

What are some best practices for decoupling interfaces from implementation?

Some best practices for decoupling interfaces from implementation include designing clear and consistent interfaces, keeping interfaces small and focused, and ensuring that interfaces and implementations are loosely coupled. It’s also important to thoroughly test each component in isolation to ensure that it functions correctly.

How does decoupling interfaces from implementation relate to object-oriented programming?

In object-oriented programming, decoupling interfaces from implementation is a key principle. It allows for the creation of objects that encapsulate their own data and behavior, providing a clear separation between the interface (how the object is used) and the implementation (how the object works internally). This makes the code more modular, easier to maintain, and more flexible.

Can decoupling interfaces from implementation help in reducing software development costs?

Yes, decoupling interfaces from implementation can potentially reduce software development costs. By making the software more modular and maintainable, it can reduce the time and effort required for debugging and testing. Additionally, it can make it easier to update or expand the software in the future, potentially saving costs in the long term.

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.

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