PHP class constructor in interface or class

23,718

Solution 1

I don't personally think you should put the constructor in the interface because you'd never create a new object by calling the constructor without being aware of which implementation you're using.

There is a mistake in your code, the method in an interface cannot have an implementation, it needs to be just a declaration, like this

interface IDataItem
{
    public function saveItem($theItem);
}

Solution 2

While it is technically possible to add the constructor to the Interface, Interfaces should not define the constructor because that would be an implementation detail of an implementing class. An Interface should just define the public API other collaborators can call upon. That is, they should not enforce a particular implementation.

If you'd put a constructor asking for a database connection in the Interface, you'd limit the concrete classes to the dependencies in the constructor signature. If a concrete class implementing the Interface needs different (because it's saving to a Webservice) or additional (maybe a Logger) dependency you cannot make that work with your Interface.

Solution 3

This is a bit off the topic, but I think it highlights a case where it might be useful to have the constructor defined in the interface.

It is possible to instantiate an object without knowing its class. Here is a simple example:

interface Animal
{
    public function __construct($name);
    public function speak();
    public function getName();
}

class Cat implements Animal
{
    protected $name=null;
    public function __construct($name){ $this->name=$name; }
    public function speak(){ echo 'meow'; }
    public function getName(){ return $this->name;}
}

class Dog implements Animal
{
    protected $name=null;
    public function __construct($name){ $this->name=$name; }
    public function speak(){ echo 'woof'; }
    public function getName(){ return $this->name;}
}

$animals=Array(array('name'=>'Felix','type'=>'Cat'),array('name'=>'Fido','type'=>'Dog'));

foreach($animals as $a)
{
    $theAnimal=new $a['type']($a['name']);
    echo '<p>speak, '.$theAnimal->getName().'</p><p>';
    $theAnimal->speak();
    echo '</p>';
}

I've used something like this for url routing, to match urls to their top-level controllers.

Solution 4

According to the PHP documentation:

"Note that it is possible to declare a constructor in an interface, what can be useful in some contexts, e.g. for use by factories."

The point is that a constructor doesn't have to be defined with concrete classes but can be defined with interfaces, eg:

interface TestInterface {
    public function __construct(interfaceA, interfaceB);
}

Then the class implementing TestInterface defines its constructor with concrete implementations of interfaceA and interfaceB:

class Test implements TestInterface {
    public function __construct(testA, testB);
}

Where testA may be something like:

class testA implements interfaceA {
    public function doStuff() {
        //...
    }
}

Solution 5

Interfaces are just a contract to enforce that classes that may be unrelated will all implement the same methods.

Since you have to explicitly know the class when constructing it, it is useless to put the constructor in the interface. You will never call the constructor through the interface. Your current solution is correct.

Share:
23,718
randomizer
Author by

randomizer

Programming in PHP and Javascript. Photographer and graphic design.

Updated on November 06, 2020

Comments

  • randomizer
    randomizer over 3 years

    I'm having some issues thinking out a good structure to build my classes and objects. In the code below I make use of an interface to define my class methods, but I also want to pass a database connection to my constructor so the class had this connection to work with. Is it correct as I coded it below that the constructor is placed within my class and the methods in my interface?

    interface IDataItem
    {
        public function saveItem(Item $theItem);
    }
    
    class DataItem implements IDataItem
    {
        public function __construct(Database $database) 
        { 
            $this->database = $database;
        }
    
        public function saveItem(Item $item) 
        {       
            //save the item
        }
    }
    
    $db = new Database(); //from a database class
    $dataItem = new DataItem($db);          
    $dataItem->saveItem($anItem);
    
    • Matthemattics
      Matthemattics over 11 years
      Why not simply make your database object a singleton?
    • GolezTrol
      GolezTrol over 11 years
      @Lübnah By making database object implement an interface, you can create a database-agnostic application that just uses the interface methods to store data, not knowing which data object instance is actually created.
    • Gordon
      Gordon over 11 years
    • Matthemattics
      Matthemattics over 11 years
      @Gordon Sigh… keep your arguments objective. I'm sure you have some valid points, but your opinionated & confrontational presentation unnecessarily obfuscates them.
    • Gordon
      Gordon over 11 years
      @Lübnah they are objective if you click the link
    • Matthemattics
      Matthemattics over 11 years
      @GolezTrol Well yes, of course. You're talking about making a Model object a la MVC-style. The Model superclass (or interface) still needs to connect to a database, though. Using a singleton (or singleton-like) pattern makes life easier because you don't need to pass around references to your DB instance whenever you want to instantiate a Model-inheriting class. Your DB instance is simply abstracted behind DB::getInstance().
    • Matthemattics
      Matthemattics over 11 years
      @Gordon Of course I clicked the link - I'm not nearly that spiteful. I'm also not at all convinced by your 2nd argument. Basically, you offer no reasoning against simply injecting your dependencies via an abstracted Singleton, as that eliminates the need to pass them via constructor arguments (which is just obnoxious). No, it's not a "pure" Singleton, but it IS still a use of the Singleton pattern.
    • Gordon
      Gordon over 11 years
      @Lübnah I consider the "normal" way to use dependencies to inject them, so I'd need reasoning for not doing that. Also, passing ctor arguments is obnoxious? Why?
    • GolezTrol
      GolezTrol over 11 years
      @Lübnah If DB:getInstance() would return a database object, you may still want to use interfaces to enforce the implementation of certain methods by that instance. Having a common ancestor just isn't always the solution, and it doesn't necessarily make things easier, at least not in this case. It's like suggesting to eat a pear if someone asks whether or not to peel apples.
    • Matthemattics
      Matthemattics over 11 years
      @Gordon Because now you have to keep track of one or more dependency instances whenever you want to instantiate a Model! The most fundamental rule of CSE is if you're doing something repeatedly, abstract it out (and do it just once). Singleton offers a way to do just that.
    • Matthemattics
      Matthemattics over 11 years
      @GolezTrol Perhaps. I'm not saying it's universally appropriate. Putting $db behind DB:getInstance() does offer some pros. That's really all I'm trying to point out.
    • Gordon
      Gordon over 11 years
      @Lübnah I am sorry but I cannot follow you. There is nothing DRY about a Singleton. You dont need a Singleton to create just once and only once.
    • Matthemattics
      Matthemattics over 11 years
      @Gordon DI actually violates DRY, if you must pass the same dependencies for each instantiation. A Singleton can provide (i) a single point of reference to an object, & (ii) a way to ensure that said object is instantiated -at all- upon referencing it. Thus, if the Database is a Singleton, instantiation can be postponed until it's actually referenced - e.g., until it's needed within the constructor. That's just one use case - surely there are more. Ergo, I don't subscribe to the "there is never a reason to use X pattern" style of reasoning. It's simply faulty.
    • Gordon
      Gordon over 11 years
      @Lübnah The point of a Singleton is to ensure/enforce instance singularity and provide a global access point. I cannot think of a situation in PHP where you would need that or where the price of tight coupling, bad maintainability, global state, hidden dependencies and single responsibility violations would outweigh the benefit. You certainly don't need a Singleton to defer object instantiation. For that, you can simply use a Factory.
    • Matthemattics
      Matthemattics over 11 years
      @Gordon I get what you're saying, & I don't necessarily disagree with your points. However, consider the simple situation. One needs a global access point, intends to instantiate just once, & wants to defer instantiation. Singleton is the lightest solution. Yes, that's a narrow use-case, but sometimes that's all one wants.
  • randomizer
    randomizer over 11 years
    Yes I'm sorry, that was a typo indeed, fixed it in the original post.
  • randomizer
    randomizer over 11 years
    Thanks, I had forgotten the explicit Item declaration in the Interface. The original post has been edited
  • Timo Huovinen
    Timo Huovinen almost 5 years
    This should be the accepted answer as of 2019-07-26