- 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());
Is there such an interface define a constructor of a class? I didn’t even know...
– user28595
@Diegof note this http://ideone.com/LDJW0y
– Wallace Maxters
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.
– Ivan Ferrer