Is it good practice to use constructors (or magic methods) in interfaces?

Asked

Viewed 1,578 times

21

Well, I usually come using interfaces to define how some methods will be used.

But to be honest, I’ve never seen anyone using interfaces to define contracts for a construtor.

In a specific case I used to avoid constructor "rewriting" in a way inappropriate to a class functionality.

Example:

class Colecao implements Colecionavel{

    public function __construct(array $items){
    }

    public function obterFiltrado(){
        return new static($this->aplicaFiltro());
    }
}



interface Colecionavel{     
    // Defino isso aqui, para não afetar a chamada `new static`
    public function __construct(array $items);
    // Faz o resto aí
}

In some other cases, I’ve seen an interface being used that forces you to use magic methods.

For example:

  interface Stringable{
      public function __toString();
  }

Well, considering that magic constructors and methods are part of the "special layer" of programming language, it is a good practice to use interfaces to "force" the class to have some behavior towards them?

  • 3

    Is there such an interface define a constructor of a class? I didn’t even know...

  • 2

    @Diegof note this http://ideone.com/LDJW0y

  • 3

    It is not a good practice, because it depends a lot on the context, the good practice is to know how to fit this according to the context of the project.

6 answers

11


First I want to repeat what I always say: good practice is to do what is right for that situation. So to say that this can or cannot do, for everything, is a bad practice (with the pardon of the pun).

Introducing

Most languages do not allow this since they have chosen to have a constructor with the class name. So if someone were to create an interface with a constructor in it, the interface would only serve that class, which would lose its meaning.

PHP chose to use a generic constructor name for some reason. I would love to say that they thought well and came to the conclusion that it would treat benefit, but I’m not so sure (lie, I know it was done on the thighs, it was just one more :) I’m sorry who gets annoyed with it, but it was not I who chose to do the language like this, I’m just the messenger).

PHP only allows you to have a constructor. What I understand is precisely because you can have an external contract. That is, they traded one universal interesting resource for another one of rare and controversial use. This is PHP!

And then to solve this question suggest the use of static methods with different names to create multiple constructors. Bela gambiarra (would not be if there was no builder, would be a conscious decision).

Problem when you do it

Imagine the following scenario: you want to make a class that implements the interface ITelefone with the builder __construct(Telefone telefone) and the IEmail with the __construct(Email email). No more. Good, because it wouldn’t make any sense to have these two builders. It would be right to have a builder __construct(Telefone telefone, Email email). Now imagine the amount of combinations you could have if you had multiple properties.

All the cases where I have seen show where this is useful the correct one is not the use of the interface but of an abstract class. That would be the solution to the problem of the previous paragraph.

Wrong tool

The abstract class makes perfect sense. A constructor should have as its only function the initialization of the object. It has to do with the structure of the object. When we talk about structure, we are talking about class inheritance. When we talk about interface, we speak purely about behavior. Although it can be said that the constructor defines the behavior of creation, it is directly dependent on the structure.

We usually say to program to the interface and not to the implementation. But the constructor has to do with the implementation, so it makes little sense to put it on the interface.

Some like to simulate the structure contract at the interface (defining methods that will access specific attributes). This is interesting, but it can be tricky to get it right. It happens to be a problem for the programmer to create a suitable constructor to ensure the correct startup of all attributes necessary to satisfy all interfaces that do this.

I am critical use of OOP in PHP in several questions I answered. For technical reasons and also for "political-social" reasons. Most PHP programmers do not understand OOP well and apply everything wrong. If it’s to use OOP that does it right*.

Solution

If you need to define a contract for the constructor, do it in an abstract class. It can even provide a basic constructor implementation.

If language had no abstract class, or even real inheritance, there maybe it made sense to use the interface. But the interface would be functioning as an abstract class with no implementation and no state.

I have just noticed a flaw in the languages that have chosen to have only interface. Some interfaces should be handled in a special way without the help of language. This is not good for a language that wants robustness.

Just out of curiosity, I see a case where it might be useful, although I don’t find it that useful. Instead of being flexible, you can use the constructor interface as a limiter. Since the language only allows a constructor, requiring an empty constructor, for example, would prevent the class from creating other constructors. In addition to finding this a bad idea almost always, the programmer goes over it only by not implementing the interface in the class.

In my research I saw some people saying that this could facilitate the use of factory standard, but I didn’t see an example demonstrating this.

But if you find a case that the interface will solve better, you can use, just need to be sure.

Understand what a builder is for (goes for any language).

Magical methods

Here it seems to me to make sense, I do not know if all, would have to evaluate case by case, the __toString() certainly it is ok. They are just behaviors that the class should have. The interface is for this.

Read more where to use interfaces.

*. Over time I’ve been changing my mind, I think OOP is a sign that people don’t need PHP, the language was good when I didn’t need it, if need it, adopt a language that this is done the right way and not a gambit created to survive.

  • I don’t know if you think it’s good practice, but after all this time, I’ve come to the conclusion that I might as well choose final public function __construct(array $items). Since you’re not in favor of using the jargon of good practice for everything, what do you think of doing such a thing in this case?

  • @Wallacemaxters have no opinion formed because PHP has some idiosyncrasies and also because I don’t know what this solves.

11

  • First, I would like to inform you that what I am about to pass on to you is only my opinion, I have no great references to point out here.
  • Second, I will write the examples in C#, I know PHP, but I prefer not to commit gaffes. But the important thing is the idea, I am well aware that what I will show you here you will have how to simulate with PHP, which has resources to supply the idea presented.

Before asking why, we have to understand what the purpose of an interface is

Many programming languages have support for POO (Object Oriented Programming), which is one of the ways to program yourself using the imperative paradigm, that has a great abstraction for beginners learn to program. And the POO has four pillars that must be respected for language to be considered as an object-oriented:

  • Abstraction (I personally think this is not a pillar, but a basis)
  • Encapsulation
  • Inheritance
  • Polymorphism

The purpose of interfaces is precisely a way of meeting the need for one of these pillars, the polymorphism. And what is polymorphism? Polymorphism is the ability of something to take different forms. This is very important for creating complex systems, the ability to exchange messages between objects in a way that communicants can talk to as many different objects as possible helps us reuse as much code as possible. The use of polymorphism allows us a low degree of coupling and high degree of modularization. Follow an example:

public class Gmail
{
  public void Enviar() { /* envia um e-mail */ }
}

public class GerenciadorDeTarefas
{
  public void Finalizar(Tarefa tarefa)
  {
    tarefa.Status == EStatus.Concluido;
    this.Notificar();
  }

  private void Notificar()
  {
    new Gmail().Enviar();
  }
}

Here we have a class that is responsible for us Notify when any Task is completed. As a notification can be made in several ways (SMS, Email, Tweets, Facebook Posts, etc), we encapsulate the behavior in a private method Notify. Currently the system only works with sending emails via gmail, but do we need to support more servers? Here we have a problem, because the class Managed tasks is strongly coupled to class Gmail, That is, because the class Gmail has a low degree of polymorphism. To solve the problem, we can do:

public interface IEmail
{
  void Enviar();
}

public class Gmail : IEmail { /* implementação */ }

public class GerenciadorDeTarefas
{
  private IEmail email;

  public GerenciadorDeTarefas(IEmail email)
  {
    this.email = email;
  }

  public void Finalizar(Tarefa tarefa) { /* implementação */ }

  private void Notificar()
  {
    this.email.Enviar();
  }
}

Thus our class Managed tasks no longer has the responsibility to know what type of email the notifications will be sent to. Now he knows that only conversation with a Iemail, not specifically with the Gmail. Now if I want to email you Yahoo, Hotmail and etc, just implement the interface of Iemail and pass the class I wish to use at the time of construction of the Managed tasks.

Note that I have to remove the concrete implementations from my class of Managed tasks for the de-escalation to be real, here I removed following one of the principles of SOLID, the Dependency Inversion Principle (Control Inversion Principle), applying the technique of Constructor Injection, there are several other techniques, but the simplest is this.

Okay, I get it. But what does that have to do with my question?

Note that to solve the problem of using the polymorphism of Gmail i needed to remove the builder call Gmail class Managed tasks? The Managed tasks do not need to construct the attribute at any time email, and neither should, this is not a more class responsibility. There is no point in having an attribute with a high degree of polymorphism if I need to manage in the class itself which concrete type it will use. I’ve seen people using strategies with enums/strings to solve the problem, which does not actually solve the coupling problem. Here is an example of a bad "usage" of polymorphism:

public class GerenciadorDeTarefas()
{
  private IEmail email;

  public GerenciadorDeTarefas(ETipoEmail tipoDeEmail)
  {
    switch (tipoDeEmail)
    {
      case ETipoEmail.Gmail:
        this.email = new Gmail();
        break;
      case ETipoEmail.Yahoo:
        this.email = new Yahoo();
        break;
    }
  }
}

Why is that bad? Because every time you implement a new class you need to put it inside that constructor. Now if your code is a third-party library, a DLL, if the developer who consumes this library needs to post a new type of email, how will he do it? He can’t modify his code, it’s a DLL, NOTE: There are ways to transform a DLL back in the original code, but this is completely unfeasible to ask a developer to do extend your library.

Now if you really want to set the concrete type in the class in a smarter way, a style approach below would be more interesting (in this case it is completely unnecessary to use inheritance for something so simple, I think using the technique of Constructor Injection is simpler and more useful, but is there for knowledge):

public abstract class GerenciadorDeTarefas
{
  protected IEmail email;

  public void Finalizar(Tarefa tarefa) { /* implementação */ }

  private void Notificar()
  {
    this.email.Enviar();
  }
}

public sealed class GerenciadorDeTarefasGmail : GerenciadorDeTarefas
{
  public GerenciadorDeTarefasGmail()
  {
    this.email = new Gmail();
  }
}

public sealed class GerenciadorDeTarefasYahoo : GerenciadorDeTarefas
{
  public GerenciadorDeTarefasYahoo()
  {
    this.email = new Yahoo();
  }
}

That way whoever consumes your library can extend the abstract class and set the concrete type he wants. But again, I say again, in that case I don’t think it’s feasible to do this.

Now comes the moment of the first step of the answer: does it make sense to define a constructor in the interface? It does not help at all in polymorphism, which is the purpose of an interface. So in these scenarios I see that it is completely useless.

There is a scenario I see sense use (GENERICS):

Generics is the only case I can think of where defining a constructor on an interface would be a benefit to the polymorphism issue. Note that C# does not have support for this, so it will be a theoretical implementation only:

public interface IEmail
{
  IEmail(string message); /* __construct */
  void Enviar();
}

public class GerenciadorDeTarefas<TEmail>
  where TEmail : IEmail // Informa que o tipo genérico deve implementar IEmail
{
  public void Finalizar(Tarefa tarefa) { /* implementação */ }

  private void Notificar()
  {
    new TEmail("Tarefa concluída!").Enviar();
  }
}

I don’t see big problems in such a code. High degree of polymorphism, easy understanding and definitions of behavior on the interface. Of course, you are limiting the way the email is created, but there are some scenarios that this is very useful. The usage is also very practical, see:

var gerenciadorDeTarefasComGmail = new GerenciadorDeTarefas<Gmail>();
gerenciadorDeTarefasComGmail.Finalizar(new Tarefa());

var gerenciadorDeTarefasComYahoo = new GerenciadorDeTarefas<Yahoo>();
gerenciadorDeTarefasComYahoo.Finalizar(new Tarefa());
  • 1

    I will be honest: I will analyze it calmly when I get home... It seems that the answer is quite complete :D

  • 1

    The complete answer really isn’t, if I were to actually talk about polymorphism, I would have to go into several topics, like dynamic typing languages, Union type, Duck type, mixins, traits, etc. But I tried not to get out of your problem, which in this case is a problem more related to static typing languages. In the case of PHP, the staff knows for Type Hinting. But any doubt, question or criticism, just comment here. ;)

8

In my conception, the use of interfaces is directly linked to the behavior and structure that is expected in the classes that will inherit it. On the other hand, constructors, are only methods executed at the moment after the object is allocated in memory, I would say that they are more connected to the instance and the object treatment than the behavior and structure.

Another point we can analyze to come up with an answer to your question is that interfaces will often be inherited by more than one class, which in turn will have different behaviors. From this point of view, specifying the constructor in the interface can limit the use of the interface for future classes, depending on how to treat them.

Therefore, I believe that using interfaces to (compel) constructors to have a connection with it directly is not a good practice. Since in my view, the interface (or contract as exemplified) has the function linked to the behavior, while the constructor reads more directly to a specification when instantiating. I do not see a coherent link between the two that justifies the use of this practice.

6

  1. Interfaces should not limit but apply the capacity of the classes. Imagine that you have some geometric shapes to calculate the area. Knowing that each shape has a different way of calculating its area, you decide to separate the shapes into classes (Square, Circle...), then add an area() method for each object that must return the calculated formula from the area.

Classe Quadrado

class Quadrado
{ 
     public function area()
     {
         return pow($this->largura,2);
     }
}

circle class:

class Circulo
{
     public function area()
     {
         return pi() * pow($this->raio, 2);
     }
} 

In doing this decides to iterate objects of these forms to have the total sum of all areas:

$total = 0;
foreach($formas as $forma){
    $total += $forma->area();
}

If another professional needs to add a Triangulo class, and perhaps do not create an area method.... We have a problem, never the triangle would enter the sum above. At this time the Interface comes to save us, with its super power of coercion:

Formainterface:

Interface FormaInterface
{
    public function area();
}

With this "contract" we say that every form must have an area method, so we must implement in our classes:

class Quadrado implements FormaInterface .....
class Triangulo implements FormaInterface .....
class Circulo implements FormaInterface .....

Done this we can through Typehint receive only Objects of the same type of Interface:

public function calculaAreas(FormaInterface $formas)
{
    foreach($formas as $forma){
        $this->total += $forma->area();
    }
}

The use of an interface should be limited to this type of use only.

  1. If you want to block the rewrite by the constructor, you can set it as private and via getInstance define that the class will be used only statically:

Builder:

private static $instance;

private function __construct()
{
    // Sua inicialização 
}
public static function getInstance()
{
    if ( is_null( self::$instance ) )
    {
      self::$instance = new self();
    }
    return self::$instance;
}
  1. Your Collection class doesn’t look better this way?

:

Interface Colecionavel
{
    ... métodos pertinentes a um item colecionável.
}

class Colecao
{
    protected $itens;

    public function __construct(Colecionavel $itens)
    {
        $this->itens = $itens
    }

    // ... Métodos pertinentes a coleção
 }

It is in English, but with a little strength to understand the idea: https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design

1

In my view imposing this obligation is not necessarily wrong. It depends on the purpose of the interface in question.

If you think that all classes that will inherit that interface should be built in a certain way, then define the constructor on the interface and vice versa.

Simple as that.

Example: let’s assume that you want to define an interface for classes that will handle an employee list. The interface will serve for several lists of various companies with different fields. This interface only makes sense if you have a list to work with. So I believe it is correct to define the constructor by having to pass a list on the interface.

If on the other hand vc is defining an interface that can be expanded to calculate in lists or in objects passed to it, it makes more sense to leave the responsibility of developing the constructor for those who implement the interface.

You define what degree of freedom is most useful to those who develop the class.

1

I agree on some points and disagree on others in the answers above. It depends a lot on the case that was used, if for example all the classes that are implementing the interface need 2 parameters to be initialized, why not let contracted in the interface that they need a constructor with 2 parameters?

Let’s use another example of the magic method, which is __toString. Let’s say I want a series of classes to handle text, they will all implement the Textointerface interface. It would be wrong for me to hire the __toString() method mandatory on the interface, since all classes must have auto-cast for string?

http://php.net/manual/en/language.oop5.magic.php#Object.tostring

Browser other questions tagged

You are not signed in. Login or sign up in order to post.