In object orientation, why are interfaces useful?

39

Can anyone give a practical explanation on why interfaces are used and why they are useful to us developers?

    
asked by anonymous 29.01.2014 / 21:13

7 answers

36

Interfaces are useful because they establish contracts . If a class implements an interface you will be able to reference instances of the class by the interface having only access to the members defined in the interface. This basically means that you ensure that the class will exhibit a certain behavior without a priori knowing how this behavior is implemented. For example, think of the IComparable interface of C #, it determines which classes that implement it have a comparison method. This idea can be used for example to build a binary tree class as shown in the Microsoft Visual C # 2010 book step by step, since by signing this interface you ensure that there is some way to compare objects from that class.

That's why we talk about interfaces that establish contracts: a class implementing an interface is like signing a contract, it promises that it will have that functionality. This allows you to obey some of the SOLID principles and write codes that are poorly coupled and highly cohesive.

The first principle, Single Responsibility Principle, says that each class must have only one responsibility: to do something and do it well. But several times to do something a class has dependencies, for example managing data or sending emails. To respect this principle, these dependencies can not be encoded in the class, they must be encoded in other classes and then used in the initial class.

The problem with this is that if you instantiate the dependencies within the class you are working on you will couple those classes. If you need to change the email or data service, you will have to change there as well and this may not be interesting and involve much more work than necessary, it was just a service.

The last SOLID principle that talks about it, it is the Dependency Inversion Principle and it says in summary: "Depend on abstractions and not on concrete implementations." This basically means that if the dependencies of one of your components are concrete instances it will become more difficult for you to keep the software, you will have to tinker with a code that is very coupled.

A good example is the standard repositories for data access. You create an interface, in the case below in C #

public interface IRepositorio<T>
{
    void Adicionar(T entidade);
    void Atualizar(T entidade);
    void Excluir(T entidade);
    IQueryable<T> ListarTudo();
    T ProcurarPorID(int id);
} 

When you need data access for a certain T entity you use a concrete implementation of this interface, but referenced by it. That means you have a service that promises that allows you to perform all these operations, but does not even say how.

Then in another assembly you can implement as you want, Entity Framework, NHibernate, or etc, and in the end, to change the implementation you only have to change in one place. Of course, for you to use in practice, tell which implementation to use for which interface there are techniques such as Dependency Injection. The main thing to understand is that the interfaces serve to promise functionality.

In short: Interfaces are contracts that help you write low coupling and high cohesion codes.

    
29.01.2014 / 21:22
16

Interfaces should, as the name says, provide interfaces for manipulating objects. If a group of different objects have the same type of action as required, you implement an interface on all of these objects.

I'll give you an example in C #. Consider these three classes:

class Cachorro
{
    public string Latir() { return "Au au"; }
}

class Gato
{
    public string Miar() { return "Miau"; }
}

class Vaca
{
    public string Mugir() { return "Muu"; }
}

I know, very childish, but it will be useful to illustrate the problem. Now we go to the middle of our super system that has a module that has a list of animals and needs to know what noise it does. How do we do this without interfaces?

List<object> animais = GetAnimais(); // Animais podem ser cachorros, gatos ou vacas, nunca se sabe
foreach (var animal in animais)
{
    var cachorro = animal as Cachorro;
    if (cachorro != null) cachorro.Latir();

    var gato = animal as Gato;
    if (gato != null) gato.Miar();

    var vaca = animal as Vaca;
    if (vaca != null) vaca.Mugir();
}

The code works. But if you work somewhere like Ibama, you'll have to do the conversion and the null-check for each type of animal you need, and there may be thousands. Having thousands of lines of code is very un-legal when you can avoid it.

So let's rewrite our animals, and put an interface on them!

interface IAnimal
{
    string Falar();
}

class Cachorro : IAnimal
{
    public string Latir() { return "Au au"; }
    public string IAnimal.Falar() { return Latir(); }
}

class Gato : IAnimal
{
    public string Miar() { return "Miau"; }
    public string IAnimal.Falar() { return Miar(); }
}

class Vaca : IAnimal
{
    public string Mugir() { return "Muu"; }
    public string IAnimal.Falar() { return Mugir(); }
}

Now that use of before turns this around:

List<IAnimal> animais = GetAnimais();
foreach (var animal in animais) animal.Falar();

That will have the same effect. So you can also create 800 new animals, and only have to do the implementation of it as an IAnimal; you will not have to tinker where you use your Falar method.

Another good thing is that by changing object to IAnimal , you have a bit more secure typing.

    
29.01.2014 / 21:28
4

Read a little about project patterns that you will understand very well. But here's a basic example.

public interface RepositorioCliente {
  public void inserir(Cliente cliente);
}

public class RepositorioClienteMysql implements RepositorioCliente {

  public void inserir(Cliente cliente) {
  //faz lógica de salvar
  }

}

When you use the repository class, you will not call it directly. You will instantiate the interface.

RepositorioCliente repositorio = new RepositorioClienteMysql();

If you need to change databases, all methods in common, the new class responsible for saving in the database will have to implement. And you will only need to change the instance.

RepositorioCliente repositorio = new RepositorioClienteNovoBanco();

Of course, classes should implement the repository interface.

    
29.01.2014 / 21:21
3

I understand that the question has been answered previously, but a very simple case is you imagine a method to send an alert when a task should be executed.

We created an interface: (simple for demonstration)

public interface IAlerta
{
    void Envia();
}

And a class that uses:

public class Tarefa
{
    public string Nome { get; set; }

    public IAlerta Alerta { get; set; }
}

We can have several versions for IAlerta, in this example an alert can be sent by email or by sms, it is precisely in this scenario that the interfaces become necessary. The Task class does not need to know the implementation required to send an sms or email, it will only use the Alert.Form () method; which will be triggered by polymorphism.

Let's simulate the implementation of the two versions of IAlerta

public class AlertaEmail : IAlerta
{
    public string Email { get; set; }
    public void Envia(string mensagem)
    {
        // aqui implementamos o envio por e-mail
    }
}

public class AlertaSMS : IAlerta
{
    public string Telefone { get; set; }
    public void Envia(string mensagem)
    {
        // aqui implementamos o envio por sms
    }
}

Let's look at an example of using the 2 on-task alert implementations

AlertaEmail alertaEmail = new AlertaEmail() { Email = "[email protected]" };
AlertaSMS alertaSMS = new AlertaSMS() { Telefone = "99 9999 9999" };

Tarefa tarefa = new Tarefa();

tarefa.Alerta = alertaEmail;
tarefa.Alerta.Envia("Ops");

tarefa.Alerta = alertaSMS;
tarefa.Alerta.Envia("Ops");

Our task system works normally without having to "worry" about how the alert will be sent, nor how it is implemented internally, just waiting for an instance that implements the IAlerta interface.

Assuming we wanted a new alert version that does send a tweet, we would not need to change our task system at all. We would only expect an implementation of IAlert to do this, which you see, could be implemented by anyone, including third parties. We just need to send the interface and request the code implemented.

public class AlertaTwitter : IAlerta
{
    public string Twitter { get; set; }
    public void Envia(string mensagem)
    {
        // aqui implementamos um post no twitter
    }
}

And in our task, we would receive an instance of an object of class AlertTwitter.

AlertaTwitter alertaTwitter = new AlertaTwitter() { Twitter = "@jaspion" };

tarefa.Alerta = alertaTwitter;

There are other scenarios very similar to the one you can use, such as a system of logs, you would have an ILog Interface with a GravaLog method for example and could be implemented in several ways, such as writing a text file, writing to a bank of data, send an e-mail, among others.

    
31.01.2014 / 20:36
2

We must use interface language construction when we need to define the behavior of a family of objects, which have no common implementation.

For example, we need to define a family of iterable objects. In this case, we can have object iterators from the database, we can have data iterators from a CSV. We can have iterators directories, we can have N iterators different things whose implementation is not shareable. In this case, when we do not have common implementation, we use interface language construction.

By demonstrating this in PHP, we will assume that we need a list of users, no matter where the data will come from. The code could be as follows:

 
interface DataUsers {
    public function getUsers();
}

class RenderUsers {
    private $datausers;

    public function __construct(DataUsers $datausers){
        $this->datausers = $datausers;
    }

    public function listUsers(){
        return $this->datausers->getUsers();
    }
}

class MySQLUsers implements DataUsers {
    public function getUsers(){
        // Aqui vai a implementação para o MySQL
    }
}

class XMLUsers implements DataUsers {
    public function getUsers(){
        // Aqui vai a implementação para o XML
    }
}

class WebServiceUsers implements DataUsers {
    public function getUsers(){
        // Aqui vai a implementação para um WebService qualquer
    }
}

Given that we have an interface, the implementation may vary because we know that the return will always be the same regardless of the input.

The usage would look like this:

 
// Se você quer os dados vindos de um banco MySQL
$mysqlUsers = new MySQLUsers();
$usuarios = new RenderUsers( $mysqlUsers );
echo $usuarios->listUsers();

// Se você quer os dados vindos de um XML
$xmlUsers = new XMLUsers();
$usuarios = new RenderUsers( $xmlUsers );
echo $usuarios->listUsers();

// Se você quer os dados vindos de um WebService qualquer
$webServiceUsers = new WebServiceUsers();
$usuarios = new RenderUsers( $webServiceUsers );
echo $usuarios->listUsers();

What's important in Object Guidance is always programming for the interface, regardless of where the data will come from, which is irrelevant.

    
29.01.2014 / 22:15
2

When you want two distinct types to have the same behavior.

This is visible when we have a pattern detection of behaviors between different types, but according to these patterns could be of the same logical family. An example is when you have in your code two distinct types like Cat and Dog, but detecting similarities like the fact that the two move, emit sounds and be domestic animals, but note that each one does it in a specific way ..

In this case we draw a type Animal where we define the defaults among all subtypes of the class ( Gato , Cachorro , Galinha ) as Andar() , Falar() .

And we implement all subtypes according to their specific type of expression:

Animal: Andar() {} Falar(){}

Gato is a Animal logo

Andar() { 'anda rápido'} 
Falar() { 'Miau' }

Cachorro is Animal , logo

Andar() {'anda desajeitado' } 
Falar() { 'Au au' }

Note that by setting Animal we do not know how the animals to be implemented walk or talk. We will only know for each specific subtype that implement it.

This practice of type composition is very useful in OOP, and whenever a pattern is detected between various types there is a gain in code reuse, simplification, and elegance

In your code you can always refer to the group as Animal , or supertype, and thus reuse methods that would otherwise have to be made for each animal. decreasing reuse of your code:

  • AdicionarAnimal(Animal a)
    add all animals (Cat, Chicken, Dog .. and futures)

  • RemoverAnimal(Animal a)
    remove all animals

Did you imagine having to create such a method for each animal?

AdicionarGalinha ... AdicionarCachorro ...

So this type composition helps a lot in reusing logic, and decreasing the complexity of code.

    
29.01.2014 / 22:03
1

I would say interfaces are useful because it allows objects to depend on a definition of a class and not on an implementation of a particular class. In this way we are able to "inject / assign" to objects any implementation that follows the definition of this class.

    
06.02.2018 / 14:27