Including A Constructor In Your Interface

Today, while looking through a pull request containing PHP code, I stumbled over an issue that is mostly non-existent in other languages, so it might be difficult to find reliable information on best practices regarding this specific topic. I was, of course, delighted to see that the code in question included an interface and its related standard implementation, instead of a simple class. However, the interface included a constructor as well.

When you try to find out whether you should include a constructor within interfaces, you’ll probably end up realizing that this is not even possible in most other languages. PHP is very permissive in what it allows you to put into interfaces, sometimes to the point of making you consider choices you should not even be able to consider, to begin with.

So, while you might already have guessed from that last statement that I’m opposed to the idea of having constructors in an interface, even though PHP might allow it, I want to try to explain the reasoning behind my stance.

Contract About Interaction Between Objects

Object-oriented programming is about objects, and how they communicate with each other. An interface is a contract that fixes the details about how specific objects want to be interacted with. Through the interface, an object lets you know what vocabulary it understands. So, whoever the other party is, as long as it uses the correct vocabulary, it is welcome!

I’d like to quote a very important passage by Alan Kay from a mailing list discussion he had after being disappointed in what people generally perceived to be the most important point of object-oriented programming:

Just a gentle reminder that I took some pains at the last OOPSLA to try to remind everyone that Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.

The big idea is "messaging" -- that is what the kernal of Smalltalk/Squeak is all about [...]. The Japanese have a small word -- ma -- for "that which is in between" -- perhaps the nearest English equivalent is "interstitial". The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

So, the interface does only and exclusively care about the interaction points between objects. The instantiation (which is the constructor’s responsibility) is not about how objects interact, but rather about how to turn a class (blueprint) into an object in the first place. At the point where the constructor comes into play, you don’t even have an object yet.

Devoid Of Implementation Details

The constructor is used to put an object into an initialized state when it is instantiated. The current state of an object, and whether it is considered valid or not, is an implementation detail, and should therefore not be relevant for the interface.

The internal properties that define the state of an object should be inaccessible to outside objects. As the constructor should only deal with these internal properties, all of its work should therefore also be hidden from external objects as well.

If you get passed a View  object, and you want to render that view, you should not need to know whether that specific object had also asked for a Logger  when being instantiated.

As Lean As Possible

The Interface Segregation Principle (ISP), which is the I in SOLID, states that an interface should only contain the methods needed for one specific role. So, as an (extremely simplified) example, when creating an interface for a Validator  object, the interface probably should only contain the single method validate() .

If the interface contains too many methods, this probably points to a design issue where your interface just mirrors your classes. This might lead to code where different responsibilities are so tightly coupled that you can’t make changes to one without affecting the other.

Consider Dependency Injection

When using dependency injection (and you really should), you usually pass in the dependencies of your object to be instantiated through its constructor. So, as an example, your console command might have an Input dependency and an Output dependency, so that it can receive input, parse it, and echo the corresponding output. This will yield a constructor along the lines of:

public function __construct(Input $input, Output $output);

Now, what if you now want to add logging to all of your console commands? You’ll need an additional dependency on a Logger as well. This will change your constructor, though. To keep the option of replacing one implementation with another one, you need to be able to adapt the constructor so that it can accept whatever dependencies you will need. And adapting the constructor should not break your existing code.

Consider Multiple Implements

If you do have your interfaces properly segregated, like described above, you might want to have a class that implements several interfaces at once. As an example, you might want to have something like this:

class CachedRenderer implements Cacheable, Renderable { }

In such a case, if both interfaces would include a constructor, they would dictate incompatible signatures for your class. You would not be able to write a constructor that would work with both, except in the rare case where their constructor is identical.

Instantiation Needs Implementations Details

The goal of using interfaces is to make your code agnostic of specific implementations. Whenever you want to call a method declared in an interface, you can just rely on the object implementing the interface to accept that method call.

Instantiation is different, though. You cannot instantiate a new object without knowing some implementation details about the specific class to use. At some point, whether it is directly in one of your classes or factories, or whether it is within an IoC container, you will need to write a statement along the lines of:

$object = new \ExactNamespace\ExactClassToInstantiate(
    $arguments,
    $thatThe,
    $exactClass,
    $needs
);

It is just not possible to instantiate a new object without knowing details about the implementation. That’s why the instantiation is usually put within factories and/or DI containers, you want to have one single place to make changes if you want to replace classes.

What About The Liskov Substitution Principle?

The Liskov Substitution Principle (LSP), which is the L in SOLID, states that wherever an object is used, you should be able to replace it with a subtype of that object without breaking the code. This means that your new object should be able to do everything that the original object could do or more.

So, what about the example we’ve given above where the subtype has a different constructor signature than the interface it implements. Won’t this break the LSP?

The LSP is not even concerned with instantiation at all. It does not state that you should be able to instantiate all classes in the same way. It only states that, when an object of a class is being used somewhere, it should be substitutable for an object of a subclass. Note the term “object” – we’re already dealing with instantiated objects at this point. The LSP is still intact, even if we have different constructors for different implementations.

What If I Want To Enforce A Specific Signature?

If you absolutely must provide a specific signature to be used as part of your API, you are already defining one part of the specific implementation, not just the contract.

In such a case, you should probably use an abstract class. Within the abstract class, you can provide as much implementation details as wanted or needed, while also marking up the parts that the extending class will need to take care of. So, you have a kind of implementation spectrum you can position your code on:

  • Interface => no implementation specifics provided
  • Abstract Class => some implementation specifics provided
  • Class => all implementation specifics provided

Conclusion

Don’t put constructors into your interface definitions. They don’t serve a valid purpose, and they might cause all sorts of issues down the road.

Did I miss something obvious? Let me know in the comments below!

6 Comments

  1. Tom on July 6, 2016 at 2:40 pm

    Everything you’ve covered in the article is great (I’m especially a fan of SOLID though I’m not as good at implementing it as I always want to be. Often, that has to do with deadlines, but that’s another discussion for another time :).

    But this is what’s interesting to me:

    Don’t put constructors into your interface definitions.

    I’ve never thought of doing so because interfaces, by definition, are meant to set up the contract between the classes which implement said interface.

    I’ve always thought of interfaces as being responsible for defining the functionality/API/whatever that:

    Must be implemented,
    Will be exposed via the class.

    In essence, it guarantees the implementation of a particular set of functions accessible to third-party clients. Period. (Is that too rigid?)

    The idea of forcing a class to have a constructor beyond the one that’s provided by default has always seemed to be a class-level detail rather than an interface detail.

    All that to say that I like your discussion around this because it shows how others might approach the problem and why they should/n’t define a constructor in the interface.Report

    • Alain Schlesser on July 6, 2016 at 2:51 pm

      I have to agree that I too have never considered using a constructor in an interface.

      However, I have gone through years of developing with languages where this was not even possible to do in the first place.

      So I assume it depends a lot on your background on whether you would want to try to do this.

      Also, I often see interfaces that are just the classes where the implementation has been ripped out. With such an approach, it is very easy to end up with a constructor in all the wrong places.

      I actually had to think a bit about this stuff when commenting on the pull request (to come up with specific reasons, not just “Nope!”), and writing the article has made it even clearer in my head.

      Also, reading your comment reminded me that not everyone is aware that “interface” is actually the “I” in “API” (Application Programming Interface). So you’re absolutely right that you want to define your API with your interfaces, and not any specific implementation details.

      Thanks for the feedback!Report

  2. David on July 8, 2016 at 11:49 am

    Did I miss something obvious?

    Maybe it’s worth to mention that this rule also applies to other magic methods that deal with the instance’s life cycle states like __destruct(), __clone(), __sleep() and so on.Report

    • Alain Schlesser on July 9, 2016 at 4:56 pm

      Hey David,

      Yes, excellent point!

      Per definition, everything that is part of the instance’s lifecycle or its internal state should not be referenced within the interface. This is not necessarily tied to magic methods, but the ones you mention are definitely part of the bunch.

      Thanks for helping fill the gaps!Report

  3. Collins Agbonghama on July 10, 2016 at 3:35 pm

    Been an avid reader of your blog though i haven’t commented once.

    Keep up the good work teaching WordPress/PHP developers OOP & best practices.

    If you ever feel nobody reads your blog, this is a comment to let you know people do.Report

    • Alain Schlesser on July 10, 2016 at 4:06 pm

      Hey Collins,

      Thanks for the feedback, and for letting me know people read my stuff. Truly appreciated!

      Cheers!Report

Leave a Comment