2010-04-23 18 views

Respuesta

106

interfaces son sólo los contratos o las firmas y que no saben nada acerca de las implementaciones .

Codificando contra medios de interfaz, el código de cliente siempre contiene un objeto de interfaz que viene de fábrica. Cualquier instancia devuelta por la fábrica sería de tipo Interfaz que cualquier clase de candidato de fábrica debe haber implementado. De esta forma, el programa del cliente no se preocupa por la implementación y la firma de la interfaz determina qué operaciones se pueden realizar. Esto se puede usar para cambiar el comportamiento de un programa en tiempo de ejecución. También te ayuda a escribir programas mucho mejores desde el punto de vista del mantenimiento.

Aquí hay un ejemplo básico para usted.

public enum Language 
{ 
    English, German, Spanish 
} 

public class SpeakerFactory 
{ 
    public static ISpeaker CreateSpeaker(Language language) 
    { 
     switch (language) 
     { 
      case Language.English: 
       return new EnglishSpeaker(); 
      case Language.German: 
       return new GermanSpeaker(); 
      case Language.Spanish: 
       return new SpanishSpeaker(); 
      default: 
       throw new ApplicationException("No speaker can speak such language"); 
     } 
    } 
} 

[STAThread] 
static void Main() 
{ 
    //This is your client code. 
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English); 
    speaker.Speak(); 
    Console.ReadLine(); 
} 

public interface ISpeaker 
{ 
    void Speak(); 
} 

public class EnglishSpeaker : ISpeaker 
{ 
    public EnglishSpeaker() { } 

    #region ISpeaker Members 

    public void Speak() 
    { 
     Console.WriteLine("I speak English."); 
    } 

    #endregion 
} 

public class GermanSpeaker : ISpeaker 
{ 
    public GermanSpeaker() { } 

    #region ISpeaker Members 

    public void Speak() 
    { 
     Console.WriteLine("I speak German."); 
    } 

    #endregion 
} 

public class SpanishSpeaker : ISpeaker 
{ 
    public SpanishSpeaker() { } 

    #region ISpeaker Members 

    public void Speak() 
    { 
     Console.WriteLine("I speak Spanish."); 
    } 

    #endregion 
} 

alt text http://ruchitsurati.net/myfiles/interface.png

Esto es sólo un ejemplo básico y explicación real del principio es más allá del alcance de esta respuesta.

EDITAR

He actualizado el ejemplo anterior y se añadió una clase base abstracta del altavoz. En esta actualización, agregué una función a todos los Spakers para "SayHello". Todos los oradores hablan "Hola mundo". Entonces esa es una característica común con funciones similares. Consulte el diagrama de clases y verá que la clase abstracta de altavoces implementa la interfaz ISpeaker y marca el Speak() como resumen, lo que significa que cada implementación de Speaker es responsable de implementar el método Speak, ya que varía de Speaker a Speaker. Pero todos los oradores dicen "Hola" por unanimidad.Entonces, en la clase de Orador, definimos un método que dice "Hola mundo" y cada implementación del Orador derivará el método SayHello.

Considere un caso en el que SpanishSpeaker no puede decir Hello, por lo que en ese caso puede anular el método SayHello para el hablante de español y presentar una excepción.

Tenga en cuenta que, hemos no realizado cambios en el Interfaz ISpeaker. Y el código de cliente y SpeakerFactory tampoco se verán afectados sin cambios. Y esto es lo que conseguimos mediante Programming-to-Interface.

Y podríamos lograr este comportamiento simplemente agregando una clase abstracta de base Altavoz y algunas modificaciones menores en Cada implementación dejando así el programa original sin cambios. Esta es una característica deseada de cualquier aplicación y hace que su aplicación sea fácil de mantener.

public enum Language 
{ 
    English, German, Spanish 
} 

public class SpeakerFactory 
{ 
    public static ISpeaker CreateSpeaker(Language language) 
    { 
     switch (language) 
     { 
      case Language.English: 
       return new EnglishSpeaker(); 
      case Language.German: 
       return new GermanSpeaker(); 
      case Language.Spanish: 
       return new SpanishSpeaker(); 
      default: 
       throw new ApplicationException("No speaker can speak such language"); 
     } 
    } 
} 

class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     //This is your client code. 
     ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English); 
     speaker.Speak(); 
     Console.ReadLine(); 
    } 
} 

public interface ISpeaker 
{ 
    void Speak(); 
} 

public abstract class Speaker : ISpeaker 
{ 

    #region ISpeaker Members 

    public abstract void Speak(); 

    public virtual void SayHello() 
    { 
     Console.WriteLine("Hello world."); 
    } 

    #endregion 
} 

public class EnglishSpeaker : Speaker 
{ 
    public EnglishSpeaker() { } 

    #region ISpeaker Members 

    public override void Speak() 
    { 
     this.SayHello(); 
     Console.WriteLine("I speak English."); 
    } 

    #endregion 
} 

public class GermanSpeaker : Speaker 
{ 
    public GermanSpeaker() { } 

    #region ISpeaker Members 

    public override void Speak() 
    { 
     Console.WriteLine("I speak German."); 
     this.SayHello(); 
    } 

    #endregion 
} 

public class SpanishSpeaker : Speaker 
{ 
    public SpanishSpeaker() { } 

    #region ISpeaker Members 

    public override void Speak() 
    { 
     Console.WriteLine("I speak Spanish."); 
    } 

    public override void SayHello() 
    { 
     throw new ApplicationException("I cannot say Hello World."); 
    } 

    #endregion 
} 

alt text http://demo.ruchitsurati.net/myfiles/interface1.png

+15

La programación en la interfaz no es ** solo ** sobre el tipo de la variable de referencia. También significa que no usa suposiciones implícitas sobre su implementación. Por ejemplo, si usa una 'Lista' como tipo, aún podría suponer que el acceso aleatorio es rápido llamando repetidamente a' get (i) '. –

+11

Las fábricas son ortogonales a la programación de interfaces, pero creo que esta explicación hace que parezca que forman parte de ella. –

+0

@Toon: estoy de acuerdo contigo. Quería proporcionar un ejemplo muy básico y simple para la programación a la interfaz. No quería confundir al interlocutor al implementar la interfaz IFlyable en algunas clases de aves y animales. –

13

Significa que debe intentar escribir su código para que use una abstracción (clase de resumen o interfaz) en lugar de la implementación directamente.

Normalmente, la implementación se inyecta en el código a través del constructor o una llamada a un método. Por lo tanto, su código conoce la interfaz o clase abstracta y puede llamar a cualquier cosa que esté definida en este contrato. Como se usa un objeto real (implementación de la interfaz/clase abstracta), las llamadas están operando en el objeto.

Este es un subconjunto de los Liskov Substitution Principle (LSP), el L de los SOLID principios.

Un ejemplo de .NET sería para codificar con IList en lugar de List o Dictionary, así que podría usar cualquier clase que implemente IList indistintamente en el código:

// myList can be _any_ object that implements IList 
public int GetListCount(IList myList) 
{ 
    // Do anything that IList supports 
    return myList.Count(); 
} 

Otro ejemplo de la biblioteca de clases base (BCL) es la clase abstracta ProviderBase; esto proporciona algo de infraestructura, y lo que es más importante significa que todas las implementaciones de proveedores se pueden usar indistintamente si se codifica en contra de ella.

+0

, pero ¿cómo puede un cliente interactuar con una interfaz y usar sus métodos vacíos? –

+0

El cliente no interactúa con la interfaz, sino a través de la interfaz :) Los objetos interactúan con otros objetos a través de métodos (mensajes) y una interfaz es una especie de lenguaje: cuando usted sabe que determinado objeto (persona) implementa (habla) inglés (IList), puede usarlo sin necesidad de saber más sobre ese objeto (que también es italiano), porque no es necesario en ese contexto (si desea pedir ayuda, no necesita saber que también habla Italiano si entiendes inglés). –

+0

BTW. El principio de sustitución de Liskov de IMHO se trata de la semántica de la herencia y no tiene nada que ver con las interfaces, que también se pueden encontrar en idiomas sin herencia (Go from Google). –

4

Esta declaración se trata de acoplamiento. Una posible razón para usar programación orientada a objetos es la reutilización. Entonces, por ejemplo, puede dividir su algoritmo entre dos objetos colaborativos A y B. Esto podría ser útil para la posterior creación de otro algoritmo, que podría reutilizar uno u otro de los dos objetos. Sin embargo, cuando esos objetos se comunican (enviar mensajes, métodos de llamada), crean dependencias entre ellos. Pero si desea usar uno sin el otro, debe especificar qué debería hacer algún otro objeto C para el objeto A si reemplazamos B. Esas descripciones se llaman interfaces. Esto permite que el objeto A se comunique sin cambios con diferentes objetos que dependen de la interfaz. La afirmación que mencionaste dice que si planeas reutilizar alguna parte de un algoritmo (o más generalmente un programa), debes crear interfaces y confiar en ellas, por lo que puedes cambiar la implementación concreta en cualquier momento sin cambiar otros objetos si usas el interfaz declarada.

26

Piense en una interfaz como un contrato entre un objeto y sus clientes. Esa es la interfaz que especifica las cosas que un objeto puede hacer y las firmas para acceder a esas cosas.

Las implementaciones son los comportamientos reales. Digamos, por ejemplo, que tienes un método sort(). Puede implementar QuickSort o MergeSort. Eso no debería importar al código de llamada del cliente, siempre y cuando la interfaz no cambie.

Las bibliotecas como la API de Java y .NET Framework hacen un uso intensivo de las interfaces porque millones de programadores usan los objetos proporcionados. Los creadores de estas bibliotecas deben tener mucho cuidado de no cambiar la interfaz de las clases en estas bibliotecas porque afectará a todos los programadores que usan la biblioteca. Por otro lado, pueden cambiar la implementación tanto como quieran.

Si, como programador, codifica en contra de la implementación, tan pronto como cambie su código deja de funcionar. Así que piense en los beneficios de la interfaz de esta manera:

  1. oculta las cosas que no necesita saber para simplificar el uso del objeto.
  2. que proporciona el contrato de cómo se comportará el objeto para que pueda confiar en que
+0

Significa que debe ser consciente de lo que está contratando el objeto: en el ejemplo provisto, solo está contratando para un tipo, no necesariamente un tipo estable. – penguat

1

interfaces de describir las capacidades.al escribir un código imperativo, hable sobre las capacidades que está utilizando, en lugar de tipos específicos o clases.

2

Como han dicho otros, significa que su código de llamada solo debe conocer un padre abstracto, NO la clase de implementación real que hará el trabajo.

Lo que ayuda a entender este es el POR QUÉ siempre debe programar a una interfaz. Hay muchas razones, pero dos de las más fáciles de explicar son

1) Pruebas.

Digamos que tengo todo el código de la base de datos en una clase. Si mi programa conoce la clase concreta, solo puedo probar mi código ejecutándolo realmente contra esa clase. Estoy usando -> para decir "habla con".

WorkerClass -> DALClass Sin embargo, agreguemos una interfaz a la mezcla.

WorkerClass -> IDAL -> DALClass.

De modo que DALClass implementa la interfaz IDAL, y la clase de trabajador SÓLO llama a través de esto.

Ahora, si queremos escribir pruebas para el código, podríamos hacer una clase simple que simplemente actúe como una base de datos.

WorkerClass -> IDAL -> IFakeDAL.

2) Reutilización

Siguiendo el ejemplo anterior, supongamos que queremos pasar de SQL Server (que utiliza nuestra DALClass hormigón) a MonogoDB. Esto requeriría un gran trabajo, pero NO si lo hemos programado en una interfaz. En ese caso, simplemente escribimos la nueva clase DB, y el cambio (a través de la fábrica)

WorkerClass -> IDAL -> DALClass

a

WorkerClass -> IDAL -> MongoDBClass

4

Si Debes escribir una Clase de Automóvil en la era de Combustión-Coche, entonces hay una gran posibilidad de que implementes OilChange() como parte de esta Clase. Pero, cuando se introducen los autos eléctricos, estarías en problemas ya que no hay cambios de aceite para estos autos, y no hay implementación.

La solución al problema es tener una interfaz performMaintenance() en la clase Car y ocultar detalles dentro de la implementación adecuada. Cada tipo de coche proporcionaría su propia implementación para performMaintenance(). Como propietario de un automóvil, todo lo que tiene que hacer es ejecutar el mantenimiento() y no preocuparse por la adaptación cuando haya un CAMBIO.

class MaintenanceSpecialist { 
    public: 
     virtual int performMaintenance() = 0; 
}; 

class CombustionEnginedMaintenance : public MaintenanceSpecialist { 
    int performMaintenance() { 
     printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n"); 
     return 0; 
    } 
}; 

class ElectricMaintenance : public MaintenanceSpecialist { 
    int performMaintenance() { 
     printf("electricMaintenance: We specialize in maintenance of Electric Cars \n"); 
     return 0; 
    } 
}; 

class Car { 
    public: 
     MaintenanceSpecialist *mSpecialist; 
     virtual int maintenance() { 
      printf("Just wash the car \n"); 
      return 0; 
     }; 
}; 

class GasolineCar : public Car { 
    public: 
     GasolineCar() { 
     mSpecialist = new CombustionEnginedMaintenance(); 
     } 
     int maintenance() { 
     mSpecialist->performMaintenance(); 
     return 0; 
     } 
}; 

class ElectricCar : public Car { 
    public: 
     ElectricCar() { 
      mSpecialist = new ElectricMaintenance(); 
     } 

     int maintenance(){ 
      mSpecialist->performMaintenance(); 
      return 0; 
     } 
}; 

int _tmain(int argc, _TCHAR* argv[]) { 

    Car *myCar; 

    myCar = new GasolineCar(); 
    myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */ 


    myCar = new ElectricCar(); 
    myCar->maintenance(); 

    return 0; 
} 

explicación adicional: Usted es propietario de un vehículo que posee varios coches. Usted crea el servicio que desea tercerizar. En nuestro caso, queremos externalizar el trabajo de mantenimiento de todos los automóviles.

  1. Identifica el contrato (interfaz) que es válido para todos sus automóviles y proveedores de servicios.
  2. Los proveedores de servicios presentan un mecanismo para proporcionar el servicio.
  3. No debe preocuparse por asociar el tipo de automóvil con el proveedor del servicio.Solo debe especificar cuándo desea programar el mantenimiento e invocarlo. La empresa de servicios apropiada debe intervenir y realizar el trabajo de mantenimiento.

    Enfoque alternativo.

  4. Identifica el trabajo (puede ser una interfaz de interfaz nueva) que es válido para todos sus automóviles.
  5. Usted sale con un mecanismo para proporcionar el servicio. Básicamente, vas a proporcionar la implementación.
  6. Invoca el trabajo y hágalo usted mismo. Aquí va a hacer el trabajo de un trabajo de mantenimiento apropiado.

    ¿Cuál es el inconveniente del segundo enfoque? Puede que no sea el experto en encontrar la mejor manera de realizar el mantenimiento. Tu trabajo es conducir el auto y disfrutarlo. No estar en el negocio de mantenerlo.

    ¿Cuál es el inconveniente del primer enfoque? Existe la sobrecarga de encontrar una empresa, etc. A menos que sea una empresa de alquiler de automóviles, puede que no valga la pena el esfuerzo.

Cuestiones relacionadas