Interface genérica como tipo de retorno?

publicado por: Anonymous

Tengo una pregunta que seguro que es muy básica. Lo que no entiendo es para que se utiliza una interfaz como tipo de retorno. Sigue siendo un tipo de la misma manera? Que utilidad tiene?

public interface ISessions {
   <IEnumerable<User>> RetrieveUser(string userId);
}

IEnumerable<User> = RetrieveUser(x);

solución

Como menciona Leandro Tuttini: Al definir una interfaz de retorno te da libertad en variar el tipo de dato concreto que devuelves sin afectar a quien invoca la funcionalidad.

A modo de adición, te voy a dar un ejemplo, considera que tienes la siguiente interfaz:

public interface IVehiculo
{
    string ObtenerPlaca();
}

La interfaz por si sola no hace gran cosa, no? Por lo que debemos crear componentes (Llamados vehiculos) para que su funcionamiento tenga sentido:

public class Carro : IVehiculo
{
    private string _Placa;
    public Carro(string placa) { _Placa = placa; }
    public string ObtenerPlaca() { return this._Placa; }
}

¡Bien!, tenemos nuestra clase Carro que resuelve el comportamiento de la interfaz IVehiculo, pero… ¿Qué tal si queremos hacer una que otra implementación?

public class Motor : IVehiculo
{
    private string _Placa;
    private string _Modelo;
    public Motor(string placa, string modelo) { _Placa = placa; _Modelo = modelo; }
    public string ObtenerPlaca() { return this._Placa; }
    public string ObtenerModelo() { return this._Modelo; }
}

¡Oh! Tenemos dos clases con la misma interfaz ¿Y una está más desarrollada que otra?

Pues, ambas heredan de IVehiculo, puedes probar el siguiente código en cualquier lugar y te funcionará de forma que todos los elementos que comparten la interfaz pueden compartir el comportamiento:

using System.Collections.Generic;

public class Program
{
    // Definimos una que otra lista para hacerla "nuestra base de datos"
    public static List<IVehiculo> Vehiculos = new List<IVehiculo>()
    {
        new Carro("123-123-12"),
        new Motor("321-32-321", "Alguna marca"),
        new Motor("22-223-210", "Otra marca"),
        new Carro("222-3-23-2")
    };

    // Definición de la  función para obtener un motor.
    public static IVehiculo ObtenerMotorPorPlaca(string placa)
    {
        foreach (IVehiculo vh in Vehiculos)
        {
            if (vh.GetType() == typeof(Motor))
                if (vh.ObtenerPlaca() == placa) return vh;
        }
        return null;
    }

    // Inicio:
    public static void Main()
    {
        IVehiculo MiVehiculo = new Motor("21132", "wwq"); // Puedo definir un motor
        Console.WriteLine("Placa del motor: " + MiVehiculo.ObtenerPlaca() + " Modelo: " + ((Motor)MiVehiculo).ObtenerModelo());

        // Asi como puedo definir un carro:
        MiVehiculo = new Carro("23232");
        Console.WriteLine("Placa del motor: " + MiVehiculo.ObtenerPlaca());

        MiVehiculo = ObtenerMotorPorPlaca("321-32-321");
        Console.WriteLine("Placa del vehiculo buscado: " + MiVehiculo.ObtenerPlaca()); 
    }
}

Si bien no es el ejemplo más practico, pero sirve para demostrar como funcionan las interfaces.

Las interfaces son algo así como un sello que se le aplica a una clase para marcar su “Comportamiento”, dicho de manera “alocada”. Una clase que implementa una interfaz, debe llevar todos los elementos de esa interfaz para desarrollar su comportamiento.

EDIT: Para aclarar algo.

Elementos con la misma interfaz implementada, tienen básicamente el mismo comportamiento, pero esto no quiere decir que sean iguales, en el ejemplo anterior he puesto una interfaz IVehiculo y otras dos clases Motor y Carro, lo he puesto por la siguiente razón:

La siguiente función devuelve un Carro o un Motor dependiendo su placa.

public static IVehiculo ObtenerMotorPorPlaca(string placa)
{
    foreach (IVehiculo vh in Vehiculos)
    {
        if (vh.ObtenerPlaca() == placa) return vh;
    }
    return null;
}

Y modificamos el código del método Main() anterior y agregamos:

public static void Main()
{
    IVehiculo MiVehiculo = new Motor("21132", "wwq"); // Puedo definir un motor
    Console.WriteLine("Placa del motor: " + MiVehiculo.ObtenerPlaca() + " Modelo: " + ((Motor)MiVehiculo).ObtenerModelo());

    // Asi como puedo definir un carro:
    MiVehiculo = new Carro("23232");
    Console.WriteLine("Placa del motor: " + MiVehiculo.ObtenerPlaca());
    Console.WriteLine(MiVehiculo.GetType()); // Carro.      

    MiVehiculo = ObtenerMotorPorPlaca("321-32-321");
    Console.WriteLine("Placa del vehiculo buscado: " + MiVehiculo.ObtenerPlaca()); 
    Console.WriteLine(MiVehiculo.GetType()); // Motor,
}

He agregado 2 GetType() para confirmar el tipo de dato usado en ambas asignaciones, la razón por la que nunca se devuelve el tipo de la interfaz, sino el tipo de clase que “firma” un contrato al implementar esa interfaz para adoptar ese comportamiento, sin embargo, pude retornar distintos tipos de una misma funcion, siempre que implementan la misma interfaz.


Algunos enlaces de referencia: Interfaces, MSDN (ingles), Uno que otro fiddle… (actualizado).

Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *