3

I just started reading more about OOP and its design patterns and is confused with this conceptual question. I am too new that I am having second thought that the proper title should be, "when is it okay to have the constructor function in the interface?"

If interface defines the blueprint/API of your object, how do you enforce or hint the developers that the objects implementing the interface requires specific dependencies/arguments to construct the object.

Because upon researching it seems like adding the constructor function in the interface is a bad practice[1][2]?

If so, it seems like the viable solution is to have the constructor function defined in an abstract class, and make that abstract class implement the interface?

interface PostValidityInterface {

    public function describe_validity();

}

abstract class PostValidityAbstract implements PostValidityInterface {

    private $post_id;
    private $validity_interval;
    private $parser;

    public function __construct( Int $post_id, \DateInterval $validity_interval  ) {
        $this->post_id = $post_id;
        $this->validity_interval = $validity_interval;
    }

    public function describe_validity() {
        $now = new DateTime( 'now' );
        $validity = $now->add( $this->validity_interval )->format('F j Y');
        return 'Post ID: ' . $this->post_id . ' is valid until ' . $validity . '.';
    }

}

class PostValidity extends PostValidityAbstract {
}

$post_validity = new PostValidity(1, new \DateInterval('P1D'));
echo $post_validity->describe_validity();

// outputs:
Post ID: 1 is valid until January 1 2019.

but it feels YAGNI to me as you have 2 things to define just to describe a Class. So I wonder if this the true correct approach? or the use of interface nor abstract is to solve a far deeper problem?

3
  • when is it okay to have the constructor function in the interface? Never. The question is: What are you trying to achieve? What's the problem to solve? Why have you added the interface to the design? Interfaces are implemented to turn objects of a given class into something else. Or in many different things.
    – Laiv
    Jan 1, 2019 at 1:04
  • 1
    YAGNI suggests not defining an interface or abstract class unless you need it, i.e. have multiple classes that implement the interface. If those classes are similar, you should factor out the common part, such that only the differences are left.
    – D Drmmr
    Jan 1, 2019 at 21:00
  • The interface is used to achieve polymorphism and nothing else. Since a constructor method is actually a static method, polymorphism does not apply because a method's invocation is only polymorphic when the type of the instance that the method is called on is determined during runtime. When you subtype a class by inheritance, you also create a situation where polymorphism applies. You can see clearly here that defining a constructor for child classes makes no sense, because it is never necessary to wait until runtime to find out the type that the method was called on. Jan 2, 2019 at 14:30

5 Answers 5

8

If interface defines the blueprint/API of your object...

No, this is where your misunderstanding starts.

An interface is not a template for a class. An interface defines a distinct, typically small, chunk of functionality. A class may implement that chunk of functionality, among other chunks of functionality defined by other interfaces. Construction is not in the picture, construction is something tied to classes and objects, there is nothing functional about construction.

Something of interest to you: the "I" in SOLID (Interface Segregation Principle).

3

I suggest you read Barbara Liskov's original paper A Behavioral Notion of Subtyping. This is the paper that established the "Liskov's Substitution Principle" and is IMHO the backbone of what OOP is and specifically what interfaces represent. It isn't very long and is quite readable.

The TL;DR though is that interfaces represent sets of behaviors that all implementors must conform to. Such behavior requirements of an existing object are independent of how, where or why the object was constructed.

2

Interface shouldn't define members or constructors (as you can see in programming languages that have this construct like java).

I would argue that dependencies for calling the function or requirements on the returned result, should be defined in the formal specification of the functions in the interface.

By using preconditions/postconditions you can signal to other developers that are trying to implement this interface, what is expected.

For example from C++:

/// @pre: Buffer != nullptr && Buffer is pointing to Size allocated bytes of memory.
/// @post: Pointers and references returned before are invalidated, in case of true.
virtual bool SetBuffer(char *Buffer, int Size) = 0;
1
  • I think this is the missing part in Martin Maats's answer, and probably what the OP really wanted to know. An interface is not necessarily a "blueprint" for a class, but if one wants to have a "blueprint", they can utilize interfaces as part of the specification. (+1)
    – Doc Brown
    Jan 2, 2019 at 8:47
2

Using an object and constructing an object can be thought of as separate concerns (though obviously related).

In part, this often occurs naturally when a portion of client code involves only usage of object(s) such that it is independent of/separated from their construction — construction being done elsewhere by different code.

For greater abstraction (if desired) we can create a separate interface and class hierarchy for each: a usage interface and implementation class(es), and a factory interface and implementation class(es).

It is, of course, possible to package a single (concrete) class as implementing both a factory and usage interface — but this is not necessary or required.

Abstract classes can be used instead of interfaces, with some advantages and some restrictions (this is also language specific).  (In some sense, you're heading toward the two interface solution using an abstract class for the factory interface, and a real interface for the usage)

Consuming clients might be abstracted to use a factory interface, though the factory implementation might directly use constructors on the object classes.  The factory can be the only code that directly invokes constructors, enhancing the separation of concerns and minimizing direct references to usage classes.


Combining construction and usage into the same interface suggests that the receiver of construction methods would be already existing objects of the same type.  While sometimes this is useful (e.g. clone an existing object, possibly with changes), generally speaking, we must provide for the creation/construction of initial objects (i.e. objects not constructed from already existing objects of the same type), so an alternate (factory) interface and/or hierarchy can be used for that.


Some languages won't even allow constructors on abstract classes, since for them, the usual override-in-subclass-mechanism doesn't work on constructors.

Using an abstract class to define a single constructor signature, and create concrete instances of subclasses might work in some languages, it would be seem rather limiting, as we might expect that concrete subclasses have the need to specify their own alternative constructors (i.e. with different signatures).

1

how do you force developers to define dependencies/arguments

With a code review and persuasive reasoning.

Trying to force me to do anything with code is just asking me to tear up your code. Sorry but only wimps let existing code push them around.

I'm all in favor of code that makes it's needs clear. At least so long as we're constructing the behavior objects. Calls to construction code should hide dependencies behind good names.

But that's my opinion. I might argue for it. I might use a language that encourages it. But I refuse to pretend that right now I know better than you will ever know. So I refuse to force my maintenance programmer to do things my way. I'll try to make it easy to do things my way. But this damn field is less than 100 years old. It revolutionizes itself so often I'd be incredibly arrogant to assume I know better then everyone who will come after me. We're all still learning.

Not the answer you're looking for? Browse other questions tagged or ask your own question.