2009-06-25 19 views
16

Todavía tengo problemas para entender para qué son buenas las interfaces. Leí algunos tutoriales y todavía no sé qué son realmente para otros, "hacen que sus clases cumplan sus promesas" y "ayudan con la herencia múltiple".Comprensión de las interfaces

Eso es todo. Todavía no sé cuándo utilizaría una interfaz en un ejemplo de trabajo real o incluso cuándo identificar cuándo usarlo.

Según mi conocimiento limitado de las interfaces, pueden ayudarme porque si algo lo implementa, puede pasar la interfaz permitiendo pasar como diferentes clases sin preocuparse de que no sea el parámetro correcto.

Pero nunca sé cuál es el verdadero objetivo de esto, ya que por lo general se detienen en este momento para mostrar lo que haría el código después de pasar la interfaz y si lo hacen parece que no hacen nada útil que podría mirar e ir "wow me ayudarían en un ejemplo del mundo real".

Así que lo que supongo que estoy diciendo es que estoy tratando de encontrar un ejemplo del mundo real donde pueda ver las interfaces en acción.

también no entienden que se puede hacer como una referencia a un objeto como éste:

ICalculator myInterface = new JustSomeClass(); 

Así que ahora si me gustaría ir MyInterface punto y intelisense iba a tirar yo sólo iba a ver los métodos de interfaz y no los otros métodos en JustSomeClass. Así que no veo un punto para esto todavía.

También comencé a hacer pruebas unitarias donde parece que les encanta usar interfaces pero todavía no entiendo por qué.

Como por ejemplo este ejemplo:

public AuthenticationController(IFormsAuthentication formsAuth) 
{ 
    FormsAuth = formsAuth ?? new FormsAuthenticationWrapper(); 
} 

public class FormsAuthenticationWrapper : IFormsAuthentication 
{ 
    public void SetAuthCookie(string userName, bool createPersistentCookie) 
    { 
     FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); 
    } 
    public void SignOut() 
    { 
     FormsAuthentication.SignOut(); 
    } 
} 

public IFormsAuthentication FormsAuth 
{ 
    get; 
    set; 
} 

Como por qué molestarse en hacer esta interfaz? ¿Por qué no simplemente hacer FormsAuthenticationWrapper con los métodos y llamarlo un día? ¿Por qué primero hacer una interfaz luego hacer que Wrapper implemente la interfaz y finalmente escribir los métodos?

Entonces no entiendo lo que dice la afirmación en realidad.

Al igual que ahora sé que la declaración está diciendo esto

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper(); 

si FormsAuth es nulo luego hacer una nueva FormsAuthenticationWrapper y asignarlo a la propiedad que es una interfaz.

Supongo que se remonta al punto completo de por qué la cosa de referencia. Especialmente en este caso ya que todos los métodos son exactamente iguales. The Wrapper no tiene ningún método nuevo que la interfaz no tenga y no estoy seguro, pero cuando haces esto, los métodos se llenan correctamente (es decir, tienen un cuerpo) y no se convierten en stubs porque eso realmente parece inútil. para mí (se convertiría a una interfaz).

A continuación, en el archivo de prueba que tienen:

var formsAuthenticationMock = new Mock<AuthenticationController.IFormsAuthentication>(); 

Así que sólo pase en el FormsAuthentication lo que estoy adivinando hace que todos los recibos falsos. Supongo que la clase contenedora se usa cuando el programa se está ejecutando, ya que tiene métodos reales que hacen algo (como firmar a una persona).

Pero al mirar el nuevo Mock (desde moq) acepta una clase o una interfaz. ¿Por qué no simplemente hizo que la clase contenedora pusiera esos métodos y luego en la nueva llamada falsa?

¿Eso no solo haría los talones para usted?

+3

Por favor, vea algunas preguntas ya respondidas: http://stackoverflow.com/questions/240152/why-would-i-want-to-use-interfaces http://stackoverflow.com/questions/444245/how-will -i-know-when-to-create-an-interface http://stackoverflow.com/questions/122883/interfaces-why-cant-i-seem-to-grasp-them – heavyd

+0

La pregunta es demasiado larga. .. –

+1

Estaba a punto de hacer esta pregunta también :) Thxs chobo2 – balexandre

Respuesta

14

Las interfaces definen contratos.

En el ejemplo que proporciona, el operador ?? solo proporciona un valor predeterminado si pasa null al constructor y realmente no tiene nada que ver con las interfaces.

Lo que es más relevante es que puede usar un objeto real FormsAuthenticationWrapper, pero también puede implementar su propio tipo IFormsAuthentication que no tiene nada que ver con la clase contenedora. La interfaz le dice qué métodos y propiedades necesita implementar para cumplir el contrato, y le permite al compilador verificar que su objeto realmente cumple con ese contrato (en cierta medida, es simple respetar un contrato en nombre, pero no en espíritu) , por lo que no tiene que usar el FormsAuthenticationWrapper preconstruido si no lo desea. Puede construir una clase diferente que funcione de forma completamente diferente pero respete el contrato requerido.

En este sentido, las interfaces son muy parecidas a la herencia normal, con una diferencia importante. En C# una clase solo puede heredar de un tipo pero puede implementar muchas interfaces. Entonces las interfaces le permiten cumplir múltiples contratos en una clase. Un objeto puede ser un objeto IFormsAuthentication y también ser algo más, como IEnumerable.

Las interfaces son aún más útiles cuando lo miras desde la otra dirección: te permiten tratar muchos tipos diferentes como si fueran todos iguales. Un buen ejemplo de esto es con las diversas clases de colecciones. Tome este ejemplo de código:

void OutputValues(string[] values) 
{ 
    foreach (string value in values) 
    { 
     Console.Writeline(value); 
    } 
} 

Esto acepta una matriz y la envía a la consola.Ahora aplicar este cambio simple de usar una interfaz:

void OutputValues(IEnumerable<string> values) 
{ 
    foreach (string value in values) 
    { 
     Console.Writeline(value); 
    } 
} 

Este código todavía toma una matriz y la envía a la consola. Pero también toma un List<string> o cualquier otra cosa que le interese darle que implementa IEnumerable<string>. Así que tomamos una interfaz y la usamos para hacer un bloque simple de código mucho más más potente.

Otro buen ejemplo es el proveedor de membresía ASP.Net. Usted le dice a ASP.Net que respeta el contrato de membresía al implementar las interfaces requeridas. Ahora puede personalizar fácilmente la autenticación ASP.Net incorporada para usar cualquier fuente, y todo gracias a las interfaces. Los proveedores de datos en el espacio de nombres System.Data funcionan de manera similar.

Una última nota: cuando veo una interfaz con una implementación de envoltura "predeterminada" como esa, considero que es un poco un anit-pattern, o al menos un olor a código. Me indica que quizás la interfaz es demasiado complicada y que es necesario dividirla o considerar usar una combinación de composición + eventos + delegados en lugar de derivación para lograr lo mismo.

10

Quizás la mejor manera de obtener una buena comprensión de las interfaces es utilizar un ejemplo del .NET framework.

considera la siguiente función:

void printValues(IEnumerable sequence) 
{ 
    foreach (var value in sequence) 
     Console.WriteLine(value); 
} 

Ahora me podría haber escrito esta función para aceptar una List<T>, object[], o cualquier otro tipo de secuencia concreta. Pero desde que escribí esta función para aceptar un parámetro de tipo IEnumerable eso significa que puedo pasar cualquier tipo concreto a esta función que implementa la interfaz IEnumerable.

La razón por la que esto es deseable es que al usar el tipo de interfaz, su función es más flexible de lo que sería de otra manera. También está aumentando la utilidad de esta función, ya que muchas personas que llaman podrán usarla sin necesidad de modificarla.

Al utilizar un tipo de interfaz, puede declarar el tipo de parámetro como un contrato de lo que necesita de cualquier tipo de concreto que se ingrese. En mi ejemplo, no me importa de qué tipo me pase, solo me importa que pueda iterarlo.

1

Utilizamos interfaces (o clases base abstractas) para permitir el polimorfismo, que es un concepto muy central en la programación orientada a objetos. Nos permite componer el comportamiento de maneras muy flexibles. Si aún no lo ha hecho, debería leer Design Patterns - contiene numerosos ejemplos de uso de interfaces.

En relación con Test Dobles (como objetos simulados), utilizamos interfaces para poder eliminar funcionalidades que actualmente no queremos probar, o que no pueden funcionar dentro de un marco de prueba de unidades.

Particularmente cuando se trabaja con desarrollo web, muchos de los servicios en los que confiamos (como el Contexto HTTP) no están disponibles cuando el código se ejecuta fuera del contexto web, pero si ocultamos esa funcionalidad detrás de una interfaz , podemos reemplazarlo con algo más durante la prueba.

+0

+1 para explicar la relevancia de las pruebas unitarias. –

+0

como la publicación, solo quería hacerle saber que puede usar HttpContext en las pruebas unitarias. Sin embargo, requiere cierto trabajo por adelantado. (ASP.MVC no tiene este problema). Básicamente es un truco en HttpContext, así que estoy de acuerdo con tratar de evitarlo. Sin embargo, si necesita refactorizar el código heredado donde HttpContext no se ha ocultado, podría ser valioso incluso si no es fácil "piratear" HttpContext. –

+0

@runefs: No lo sabía, gracias por la sugerencia. ¿Tiene un enlace que explica qué es el truco, en caso de que yo (u otra persona) lo necesite? –

16

Ok, también tuve dificultades para comprenderlo al principio, así que no se preocupe.

Piensa en esto, si tienes una clase, digamos que es un personaje de un videojuego.

public class Character 
{ 
} 

Ahora digo que quiero que el personaje tenga un arma. Podría utilizar una interfaz para determin los métodos requeridos por un arma:

interface IWeapon 
{ 
    public Use(); 
} 

por lo que permite darle al personaje un arma:

public class Character 
{ 
    IWeapon weapon; 

    public void GiveWeapon(IWeapon weapon) 
    { 
     this.weapon = weapon; 
    } 

    public void UseWeapon() 
    { 
     weapon.Use(); 
    } 
} 

Ahora podemos crear armas que utilizan la interfaz IWeapon y podemos dar a cualquier clase de personaje y esa clase puede usar el elemento.

public class Gun : IWeapon 
{ 
    public void Use() 
    { 
     Console.Writeline("Weapon Fired"); 
    } 
} 

A continuación, puede pegarse juntos:

Character bob = new character(); 
Gun pistol = new Gun(); 
bob.GiveWeapon(pistol); 
bob.UseWeapon(); 

Ahora esto es un ejemplo general, pero da una gran cantidad de energía. Puede leer más sobre esto si busca el Patrón de estrategia.

+2

GRAN EJEMPLO mate. Intento aprender a usar Interface también, pero hasta ahora tuve dificultades para hacerlo. Este simple pero como dijiste, muy poderoso ejemplo, solo me dio algunas líneas de quidlines. Realmente me gusta. Aclamaciones. –

+0

¡+1 me ayudó a salir también! He encontrado que es difícil de entender con los usos genéricos de 'foo',' bar', etc., pero este simple ejemplo temático me ha ayudado al menos a entender un poco más – Curt

+0

6 años después de aprender sobre interfaces ¡¡¡POR FIN LO OBTENGO AHORA! ¡Gracias amigo por ese ejemplo! – Austin

1

La forma entendía que era:

derivación es 'es-a' relación por ejemplo, un perro es un animal, una vaca es un animal, sino una interfaz no se deriva, que se implementa.

Entonces, la interfaz es una relación 'posible', por ejemplo, un perro puede ser un perro espía, un perro puede ser un perro de circo, etc. Pero para lograr esto, un perro tiene que aprender algunas cosas específicas. Lo que en la terminología de OO significa que su clase tiene que poder hacer algunas cosas específicas (contrato como lo llaman) si implementa una interfaz. Por ejemplo, si su clase implementa IEnumerable, significa claramente que su clase tiene (debe tener) una funcionalidad tal que sus objetos se pueden enumerar.

Por lo tanto, en esencia, a través de la implementación de la interfaz, una clase expone una funcionalidad a sus usuarios que puede hacer algo y NO es herencia.

0
ICalculator myInterface = new JustSomeClass(); 
JustSomeClass myObject = (JustSomeClass) myInterface; 

Ahora tiene dos "interfaces" para trabajar con el objeto.

Soy bastante nuevo en esto también, pero me gusta pensar en las interfaces como botones en un control remoto. Al usar la interfaz de ICalculator, solo tiene acceso a los botones (funcionalidad) previstos por el diseñador de la interfaz. Cuando utiliza la referencia de objeto JustSomeClass, tiene otro conjunto de botones. Pero ambos apuntan al mismo objeto.

Hay muchas razones para hacer esto. El que me ha sido más útil es la comunicación entre compañeros de trabajo. Si pueden acordar una interfaz (botones que se presionarán), un desarrollador puede trabajar en la implementación de la funcionalidad del botón y otro puede escribir el código que usa los botones.

Espero que esto ayude.

1

Con casi todo lo escrito sobre interfaces, déjame intentarlo.

En términos simples, la interfaz es algo que relacionará dos o más, de lo contrario, las clases no relacionadas.

Las interfaces definen el contrato que asegura que dos o más clases, aunque no estén completamente relacionadas, implementen una interfaz común, contendrán un conjunto común de operaciones.

Combinado con el soporte de polimorfismo, se pueden usar interfaces para escribir código más limpio y dinámico.

por ejemplo.

livingBeings de interfaz

- Hablar() // dice cualquiera que ES un livingBeing necesita definir cómo hablan

perro clase implementa livingBeings

--speak() {corteza ;} // implementación de hablar como un perro

aves clase implementa livingBeings

--speak() {chirp;} // implementación de speak como pájaro

+0

Esto es diferente de la herencia, ¿cómo? – Radmation

+0

@RadleyAnaya La herencia es más una extensión (de estado y comportamiento); la interfaz es puramente sobre comportamiento ... – Nrj

2

Le daré un ejemplo a continuación pero permítame comenzar con una de sus declaraciones. "No sé cómo identificar cuándo usar uno". ponerlo al borde.No necesita identificar cuándo usarlo sino cuándo no usarlo. Cualquier parámetro (al menos para los métodos públicos), cualquier propiedad (pública) (y personalmente yo extendería la lista a cualquier cosa) debería declararse como una especie de interfaz, no como una clase específica. La única vez que alguna vez declararía algo de un tipo específico sería cuando no había una interfaz adecuada.

me gustaría ir

IEnumerable<T> sequence; 

, cuando cada vez que puedo y casi nunca (el único caso que se me ocurre es que si realmente necesitaba el método ParaCada)

List<T> sequence; 

y ahora un ejemplo . Supongamos que está creando un sistema que puede comparar precios en automóviles y computadoras. Cada uno se muestra en una lista.

Los precios de los automóviles se basan en datos de un conjunto de sitios web y los precios de las computadoras de un conjunto de servicios.

una solución podría ser: crear una página Web, por ejemplo con una cuadrícula de datos y la inyección de dependencias de un IDataRetriever (donde IDataRetriver es alguna de las interfaces haciendo de captación de datos disponibles sin ti tener que saber dónde vienen los datos (DB, XML, servicios web o ...) o cómo se obtuvieron (datos extraídos, consulta SQL en datos internos o lectura del archivo).

Dado que los dos escenarios tenemos nada más que el uso en común, una superclase tiene poco sentido, pero la página que usa nuestras dos clases (una para autos y una para computadoras) necesita realizar exactamente las mismas operaciones en ambos casos para que sea posible, necesitamos decirle a la página (compilador) qué operaciones son posibles. que por medio de un n interfaz y luego las dos clases implementan esa interfase.

el uso de la inyección de dependencia no tiene nada que ver con cuándo o cómo usar las interfaces, pero la razón por la que lo incluí es otro escenario común donde las interfaces te hacen la vida más fácil. Pruebas. Si usa inyección e interfaces, puede sustituir fácilmente una clase de producción por una clase de prueba cuando realice la prueba. (Esto podría ser cambiar almacenes de datos o forzar un error que podría ser muy difícil de producir en el código de lanzamiento, digamos una condición de carrera)

5

Todas las respuestas aquí han sido útiles y dudo que pueda agregar algo nuevo a la mezcla, pero al leer las respuestas aquí, dos de los conceptos mencionados en dos respuestas diferentes realmente encajaron bien en mi cabeza, así que voy a componer mi comprensión aquí con la esperanza de que pueda ser de ayuda.

Una clase tiene métodos y propiedades, y cada uno de los métodos y propiedades de una clase tiene una firma y un cuerpo

public int Add(int x, int y) 
{ 
return x + y; 
} 

La firma del método Add es todo antes de que el primer carácter llave de

public int Add(int x, int y) 

El objetivo de la firma del método es asignar un nombre a un método y también describir su nivel de protección (público, protegido, interno, privado y/o virtual) que define dónde se puede acceder a un método en el código

La firma también define el tipo del valor devuelto por el método, el método Add anterior devuelve un int, y los argumentos de un método espera haber pasado a ella por las personas que llaman

métodos son generalmente considerados como algo un objeto puede hacer, el ejemplo anterior implica que la clase el método se define en trabajos con los números

El cuerpo del método describe exactamente (en código) cómo es que un objeto realiza la acción descrita por el nombre del método. En el ejemplo anterior, el método add funciona aplicando el operador de suma a sus parámetros y ajustando el resultado.

Una de las principales diferencias entre una interfaz y una clase en términos de sintaxis de lenguaje es que una interfaz solo puede definir la firma de un método, nunca el cuerpo del método.

Dicho de otra manera, una interfaz puede describir en las acciones (métodos) de una clase, pero nunca debe describir cómo se realizará una acción.

Ahora que con un poco de suerte comprendes mejor qué es una interfaz, podemos pasar a la segunda y tercera parte de tu pregunta cuando, y por qué usaríamos una interfaz en un programa real.

Una de las principales interfaces de tiempo que se utilizan en un programa es cuando uno quiere realizar una acción, sin querer saber, o estar vinculado a los detalles de cómo se realizan esas acciones.

Ese es un concepto muy abstracto a grapsp así que quizás un ejemplo podría ayudar a concretar las cosas en su mente

Imagínese usted es el autor de un navegador web muy popular que millones de personas utilizan cada día y tiene miles de solicitudes de características de personas, algunas grandes, otras pequeñas, algunas buenas y otras como "devuelva <maquee> y <blink> asistencia".

ya que sólo tiene un relitivly pequeño número de desarrolladores, y un número aún menor de horas en el día, no se puede poner en práctica todas las características posiblemente solicitado a sí mismo, pero que todavía quiere satisfacer a sus clientes

Así usted decide permitir que los usuarios desarrollen sus propios complementos, para que puedan <blink 'hasta que las vacas vuelvan a casa.

Para implementar esto podría llegar a una clase de complemento que se parece a:

public class Plugin 
{ 
public void Run (PluginHost browser) 
{ 
//do stuff here.... 
} 
} 

Pero, ¿cómo se puede aplicar razonablemente que el método?No es posible saber cómo Precisa todos los plugins futuro poossible va a funcionar

Una posible forma de evitar esto es definir Plugin como una interfaz y que el navegador se refieren a cada plugin usando que, de esta manera:

public interface IPlugin 
{ 
void Run(PluginHost browser); 
} 

public class PluginHost 
{ 
public void RunPlugins (IPlugin[] plugins) 
{ 
foreach plugin in plugins 
{ 
plugin.Run(this); 
} 
} 
} 

Tenga en cuenta que como se discutió anteriormente, la interfaz IPlugin describe el método Run pero no especifica cómo funciona Run, porque es específico de cada complemento, no queremos que el plugin se ocupe de los detalles de cada plugin individual.

Para demostrar el aspecto de "puede ser un" de la relación entre una clase y una interfaz, escribiré un complemento para el complemento siguiente que implementa la etiqueta <blink>.

public class BlinkPlugin: IPlugin 
{ 
private void MakeTextBlink(string text) 
{ 
//code to make text blink. 
} 
public void Run(PluginHost browser) 
{ 
MakeTextBlink(browser.CurrentPage.ParsedHtml); 
} 
} 

Desde esta perspectiva se puede ver que el plugin se define en una clase llamada BlinkPlugin sino porque también implementa la interfaz del IPlugin también puede ser referido como un objeto IPlugin, como la clase PluginHost anterior hace, porque no sabe ni le importa qué tipo es realmente la clase, solo que puede ser un IPlugin

Espero que esto haya ayudado, realmente no tenía la intención de que fuera tan largo.

+0

¡Encontré tu respuesta para ser la que encendió la bombilla! – callisto