Pregunta ¿Qué significa "programar en una interfaz"?


He visto esto mencionado algunas veces y no tengo claro lo que significa. ¿Cuándo y por qué harías esto?

Sé lo que hacen las interfaces, pero el hecho de que no tengo claro esto me hace pensar que me estoy perdiendo de usarlas correctamente.

¿Es así si lo hicieras?

IInterface classRef = new ObjectWhatever()

Podrías usar cualquier clase que implemente IInterface? ¿Cuándo necesitarías hacer eso? Lo único que se me ocurre es que si tienes un método y no estás seguro de qué objeto se pasará, espera que se implemente. IInterface. No puedo pensar con qué frecuencia necesitarías hacer eso ... (Además, ¿cómo podrías escribir un método que incluya un objeto que implemente una interfaz? ¿Es posible?)

Lo siento si me perdí completamente el punto.


709
2017-12-21 00:48


origen


Respuestas:


Aquí hay algunas respuestas maravillosas a estas preguntas que entran en todo tipo de detalles sobre las interfaces y el código de acoplamiento flexible, inversión de control, etc. Hay algunas discusiones bastante embriagadoras, por lo que me gustaría aprovechar la oportunidad para analizar un poco las cosas para comprender por qué una interfaz es útil.

Cuando comencé a exponerme a las interfaces, yo también estaba confundido acerca de su relevancia. No entendí por qué los necesitabas. Si estamos usando un lenguaje como Java o C #, ya tenemos herencia y veo las interfaces como más débil forma de herencia y pensamiento, "¿por qué molestarse?" En cierto sentido tenía razón, se puede pensar en las interfaces como una especie de herencia débil, pero más allá de eso, finalmente entendí su uso como una construcción del lenguaje al pensar en ellas como un medio para clasificar los rasgos o comportamientos comunes que fueron exhibidos por potencialmente muchas clases de objetos no relacionados.

Por ejemplo, supongamos que tiene un juego SIM y tiene las siguientes clases:

 class HouseFly inherits Insect {
   void FlyAroundYourHead(){}
   void LandOnThings(){}
 }

 class Telemarketer inherits Person {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}
 }

Claramente, estos dos objetos no tienen nada en común en términos de herencia directa. Pero, podrías decir que ambos son molestos.

Digamos que nuestro juego necesita tener algún tipo de azar cosa que molesta al jugador cuando cenan. Esto podría ser un HouseFly o una Telemarketer o ambos, pero ¿cómo se permiten ambos con una sola función? ¿Y cómo le pides a cada tipo de objeto diferente que "haga su cosa molesta" de la misma manera?

La clave para darse cuenta es que tanto Telemarketer y HouseFly comparten un comportamiento común sin interpretación, aunque no se parecen en términos de modelarlos. Entonces, hagamos una interfaz que ambos puedan implementar:

 interface IPest {
    void BeAnnoying();
 }

 class HouseFly inherits Insect implements IPest {
   void FlyAroundYourHead(){}
   void LandOnThings(){}

   void BeAnnoying() {
     FlyAroundYourHead();
     LandOnThings();
   }
 }

 class Telemarketer inherits Person implements IPest {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}

   void BeAnnoying() {
      CallDuringDinner();
      ContinueTalkingWhenYouSayNo();
   }
 }

Ahora tenemos dos clases que pueden ser molestas a su manera. Y no necesitan derivar de la misma clase base y compartir características inherentes comunes: simplemente necesitan cumplir el contrato de IPest - ese contrato es simple. Sólo tienes que BeAnnoying. En este sentido, podemos modelar lo siguiente:

 class DiningRoom {

   DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

   void ServeDinner() {
     when diningPeople are eating,

       foreach pest in pests
         pest.BeAnnoying();
   }
 }

Aquí tenemos un comedor que acepta varios comensales y varias plagas, tenga en cuenta el uso de la interfaz. Esto significa que en nuestro pequeño mundo, un miembro de la pestsmatriz en realidad podría ser una Telemarketer objeto o una HouseFly objeto.

los ServeDinner El método se llama cuando se sirve la cena y se supone que nuestra gente en el comedor debe comer. En nuestro pequeño juego, es cuando nuestras plagas hacen su trabajo: cada plaga es instruida para ser molesta a través del IPest interfaz. De esta manera, podemos tener ambos fácilmente Telemarketers y HouseFlys ser molesto en cada una de sus formas; solo nos importa que tengamos algo en el DiningRoom objeto que es una plaga, realmente no nos importa lo que es y no podrían tener nada en común con los demás.

Este ejemplo de pseudocódigo muy artificial (que se prolongó mucho más de lo que esperaba) simplemente pretende ilustrar el tipo de cosas que finalmente me iluminaron en términos de cuándo podríamos usar una interfaz. Me disculpo de antemano por la tontería del ejemplo, pero espero que ayude en su comprensión. Y, para estar seguro, las otras respuestas publicadas que ha recibido aquí realmente abarcan toda la gama del uso de las interfaces hoy en día en los patrones de diseño y las metodologías de desarrollo.


1402
2017-12-21 03:43



El ejemplo específico que solía dar a los estudiantes es que deben escribir

List myList = new ArrayList(); // programming to the List interface

en lugar de

ArrayList myList = new ArrayList(); // this is bad

Estos se ven exactamente iguales en un programa corto, pero si continúas usando myList 100 veces en su programa, puede comenzar a ver la diferencia. La primera declaración garantiza que solo llame a métodos en myList que están definidos por el List interfaz (entonces no ArrayList métodos específicos). Si ha programado la interfaz de esta manera, más adelante puede decidir que realmente necesita

List myList = new TreeList();

y solo tienes que cambiar tu código en ese punto. Usted ya sabe que el resto de su código no hace nada que se romperá cambiando el implementación porque has programado para interfaz.

Los beneficios son aún más obvios (creo) cuando se habla de parámetros de método y valores devueltos. Toma esto por ejemplo:

public ArrayList doSomething(HashMap map);

Esa declaración de método lo vincula a dos implementaciones concretas (ArrayList y HashMap) Tan pronto como se llame a ese método desde otro código, cualquier cambio en esos tipos probablemente signifique que también tendrá que cambiar el código de llamada. Sería mejor programar a las interfaces.

public List doSomething(Map map);

Ahora no importa qué tipo de List regresas, o qué tipo de Map se pasa como un parámetro. Cambios que hace dentro del doSomething método no lo forzará a cambiar el código de llamada.


225
2017-12-21 01:35



La programación en una interfaz dice: "Necesito esta funcionalidad y no me importa de dónde viene".

Considere (en Java), el List interfaz frente a la ArrayList y LinkedList clases concretas Si todo lo que me importa es que tengo una estructura de datos que contiene múltiples elementos de datos a los que debería acceder por iteración, escogería un List (y eso es el 99% del tiempo). Si sé que necesito insertar / eliminar a tiempo constante de cualquier extremo de la lista, podría elegir el LinkedList implementación concreta (o más probable, use el Colainterfaz). Si sé que necesito acceso aleatorio por índice, elegiría el ArrayList clase concreta.


61
2017-12-21 00:59



El uso de interfaces es un factor clave para hacer que su código sea fácilmente comprobable además de eliminar los acoplamientos innecesarios entre sus clases. Al crear una interfaz que define las operaciones en su clase, permite que las clases que desean usar esa funcionalidad puedan usarla sin depender directamente de su clase de implementación. Si más adelante decide cambiar y utilizar una implementación diferente, solo necesita cambiar la parte del código donde se crea una instancia de la implementación. El resto del código no necesita cambiar porque depende de la interfaz, no de la clase implementadora.

Esto es muy útil para crear pruebas unitarias. En la clase bajo prueba, se depende de la interfaz e inyecta una instancia de la interfaz en la clase (o una fábrica que le permite crear instancias de la interfaz según sea necesario) a través del constructor o un conjunto de propiedades. La clase usa la interfaz proporcionada (o creada) en sus métodos. Cuando vaya a escribir sus pruebas, puede simular o falsificar la interfaz y proporcionar una interfaz que responda con los datos configurados en la prueba de su unidad. Puedes hacer esto porque tu clase bajo prueba trata solo con la interfaz, no con tu implementación concreta. Cualquier clase que implemente la interfaz, incluidas las clases falsa o falsa, funcionará.

EDITAR: A continuación se muestra un enlace a un artículo donde Erich Gamma analiza su cita, "Programa a una interfaz, no una implementación".

http://www.artima.com/lejava/articles/designprinciples.html


35
2017-12-21 01:20



Deberías mirar Inversion of Control:

En tal escenario, no escribirías esto:

IInterface classRef = new ObjectWhatever();

Usted escribiría algo como esto:

IInterface classRef = container.Resolve<IInterface>();

Esto entraría en una configuración basada en reglas en container objeto, y construir el objeto real para usted, que podría ser ObjectWhatever. Lo importante es que podría reemplazar esta regla por algo que utilizara por completo otro tipo de objeto, y su código aún funcionaría.

Si dejamos IoC fuera de la mesa, puede escribir un código que sepa que puede hablar con un objeto eso hace algo específico, pero no qué tipo de objeto o cómo lo hace.

Esto sería útil al pasar parámetros.

En cuanto a su pregunta entre paréntesis "Además, ¿cómo podría escribir un método que tenga en un objeto que implemente una interfaz? ¿Es eso posible?", En C # simplemente usaría el tipo de interfaz para el tipo de parámetro, como este:

public void DoSomethingToAnObject(IInterface whatever) { ... }

Esto se conecta directamente a la "conversación con un objeto que hace algo específico". El método definido anteriormente sabe qué esperar del objeto, que implementa todo en IInterface, pero no le importa qué tipo de objeto es, solo que se adhiere al contrato, que es lo que es una interfaz.

Por ejemplo, probablemente esté familiarizado con las calculadoras y probablemente haya utilizado algunas en sus días, pero la mayoría de las veces son todas diferentes. Usted, por otro lado, sabe cómo debería funcionar una calculadora estándar, por lo que puede usarlas todas, incluso si no puede usar las características específicas que tiene cada calculadora y ninguna de las otras tiene.

Esta es la belleza de las interfaces. Puede escribir un fragmento de código que sepa que obtendrá objetos pasados ​​de los que puede esperar cierto comportamiento. No le importa qué tipo de objeto es, solo que admite el comportamiento necesario.

Déjame darte un ejemplo concreto.

Tenemos un sistema de traducción personalizado para formularios de Windows. Este sistema recorre los controles de un formulario y traduce texto en cada uno. El sistema sabe cómo manejar los controles básicos, como el tipo de control que tiene una propiedad de texto, y cosas básicas similares, pero para todo lo básico, se queda corto.

Ahora, dado que los controles heredan de clases predefinidas sobre las que no tenemos control, podríamos hacer una de estas tres cosas:

  1. Cree soporte para nuestro sistema de traducción para detectar específicamente con qué tipo de control está trabajando y traduzca los bits correctos (pesadilla de mantenimiento)
  2. Crear soporte en clases base (imposible, ya que todos los controles heredan de diferentes clases predefinidas)
  3. Añadir compatibilidad con la interfaz

Así que hicimos nr. 3. Todos nuestros controles implementan ILocalizable, que es una interfaz que nos brinda un método, la capacidad de traducir "sí mismo" en un contenedor de texto / reglas de traducción. Como tal, el formulario no necesita saber qué tipo de control ha encontrado, solo que implementa la interfaz específica, y sabe que hay un método donde puede llamar para localizar el control.


34
2017-12-21 00:58



La programación en una interfaz no tiene absolutamente nada que ver con interfaces abstractas como las que vemos en Java o .NET. Ni siquiera es un concepto de OOP.

Lo que realmente significa es no meterse con las partes internas de un objeto o estructura de datos. Use la Interfaz de programa abstracta, o API, para interactuar con sus datos. En Java o C # eso significa usar propiedades y métodos públicos en lugar de acceso de campo sin procesar. Para C eso significa usar funciones en lugar de punteros sin procesar.

EDITAR: Y con las bases de datos, significa usar vistas y procedimientos almacenados en lugar de acceso directo a la tabla.


28
2017-07-19 19:34



Código a la interfaz. No la implementación no tiene NADA que ver con Java, ni su construcción de interfaz. 

Este concepto se destacó en los libros Patterns / Gang of Four, pero probablemente fue mucho antes. El concepto ciertamente existió mucho antes de que Java existiera. 

La construcción de la interfaz de Java se creó para ayudar en esta idea (entre otras cosas), y las personas se han centrado demasiado en la construcción como el centro del significado en lugar de la intención original. Sin embargo, es la razón por la que tenemos métodos y atributos públicos y privados en Java, C ++, C #, etc.

Significa simplemente interactuar con la interfaz pública de un objeto o sistema. No se preocupe o incluso anticipe cómo hace lo que hace internamente. No te preocupes por cómo se implementa. En código orientado a objetos, es por eso que tenemos métodos / atributos públicos vs. privados. Estamos destinados a utilizar los métodos públicos porque los métodos privados solo están ahí para su uso interno, dentro de la clase. Constituyen la implementación de la clase y se pueden cambiar según sea necesario sin cambiar la interfaz pública. Supongamos que con respecto a la funcionalidad, un método en una clase realizará la misma operación con el mismo resultado esperado cada vez que lo llame con los mismos parámetros. Permite al autor cambiar la forma en que funciona la clase, su implementación, sin romper la forma en que las personas interactúan con ella.

Y puede programar en la interfaz, no en la implementación sin usar una construcción de interfaz. Puede programar en la interfaz, no la implementación en C ++, que no tiene una construcción de interfaz. Puede integrar dos sistemas empresariales masivos de forma mucho más sólida siempre que interactúen a través de interfaces públicas (contratos) en lugar de llamar a métodos en objetos internos de los sistemas. Se espera que las interfaces siempre reaccionen de la misma manera esperada dados los mismos parámetros de entrada; si se implementa en la interfaz y no en la implementación. El concepto funciona en muchos lugares.

Sacude la idea de que Java Interfaces tiene algo que hacer con el concepto de 'Programa para la interfaz, no la implementación'. Pueden ayudar a aplicar el concepto, pero son no el concepto.


21
2018-04-14 19:36



Parece que entiende cómo funcionan las interfaces, pero no está seguro de cuándo usarlas y qué ventajas ofrecen. Aquí hay algunos ejemplos de cuándo una interfaz tendría sentido:

// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.

interface ISearchProvider
{
    string Search(string keywords);
}

entonces podría crear GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider, etc.

// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
    void Download(string url)
}

// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
    Bitmap LoadImage(string filename)
}

luego crea JpegImageLoader, GifImageLoader, PngImageLoader, etc.

La mayoría de los complementos y los sistemas de complementos funcionan fuera de las interfaces.

Otro uso popular es para el patrón Repositorio. Supongamos que quiero cargar una lista de códigos postales de diferentes fuentes

interface IZipCodeRepository
{
    IList<ZipCode> GetZipCodes(string state);
}

entonces podría crear un XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository, etc. Para mis aplicaciones web, a menudo creo repositorios XML desde el principio para poder tener algo en funcionamiento antes de que la Base de Datos SQL esté lista. Una vez que la base de datos está lista escribo un SQLRepository para reemplazar la versión XML. El resto de mi código permanece sin cambios, ya que se ejecuta fuera de las interfaces.

Los métodos pueden aceptar interfaces tales como:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

12
2017-12-21 04:45