Interfaces C # – ¿Cuál es el punto?

El motivo de las interfaces realmente me elude. Por lo que entiendo, es una solución alternativa para la herencia múltiple inexistente que no existe en C # (o eso me dijeron).

Todo lo que veo es que predefinas algunos miembros y funciones, que luego deben redefinirse en la clase nuevamente. Por lo tanto, hacer que la interfaz sea redundante. Simplemente se siente como sintáctico … bueno, basura para mí (por favor, sin ofenderse significaba basura como en cosas inútiles).

En el ejemplo dado a continuación tomado de un hilo de interfaces C # diferentes en el desbordamiento de la stack, solo crearía una clase base llamada Pizza en lugar de una interfaz.

ejemplo fácil (tomado de una contribución de desbordamiento de stack diferente)

public interface IPizza { public void Order(); } public class PepperoniPizza : IPizza { public void Order() { //Order Pepperoni pizza } } public class HawaiiPizza : IPizza { public void Order() { //Order HawaiiPizza } } 

El punto es que la interfaz representa un contrato . Un conjunto de métodos públicos que cualquier clase implementadora debe tener. Técnicamente, la interfaz solo rige la syntax, es decir, qué métodos hay, qué argumentos obtienen y qué devuelven. Usualmente encapsulan la semántica también, aunque eso solo por la documentación.

A continuación, puede tener diferentes implementaciones de una interfaz y cambiarlas a voluntad. En su ejemplo, dado que cada instancia de pizza es una IPizza , puede usar IPizza donde sea que maneje una instancia de un tipo de pizza desconocida. Se garantiza que todas las instancias cuyo tipo herede de IPizza pueden ser ordenadas, ya que tiene un método Order() .

Python no tiene tipado estático, por lo tanto, los tipos se guardan y se buscan en el tiempo de ejecución. Entonces puede intentar llamar a un método Order() en cualquier objeto. El tiempo de ejecución es feliz siempre y cuando el objeto tenga tal método y probablemente solo se encoja de hombros y diga “Meh” si no lo hace. No es así en C #. El comstackdor es responsable de realizar las llamadas correctas y si solo tiene algún object aleatorio, el comstackdor aún no sabe si la instancia durante el tiempo de ejecución tendrá ese método. Desde el punto de vista del comstackdor, no es válido porque no puede verificarlo. (Puedes hacer tales cosas con la reflexión o la palabra clave dynamic , pero eso está yendo un poco lejos ahora, supongo).

También tenga en cuenta que una interfaz en el sentido habitual no necesariamente tiene que ser una interface C #, podría ser una clase abstracta o incluso una clase normal (que puede ser útil si todas las subclases necesitan compartir algún código común – en la mayoría casos, sin embargo, la interface suficiente).

Nadie realmente ha explicado en términos sencillos cómo las interfaces son útiles, así que voy a darle una oportunidad (y robar una idea de la respuesta de Shamim un poco).

Tomemos la idea de un servicio de pedido de pizza. Puede tener varios tipos de pizzas y una acción común para cada pizza es preparar el pedido en el sistema. Cada pizza tiene que estar preparada, pero cada pizza se prepara de forma diferente . Por ejemplo, cuando se ordena una pizza de masa rellena, el sistema probablemente deba verificar que ciertos ingredientes estén disponibles en el restaurante y ponerlos a un lado que no sean necesarios para las pizzas de platos hondos.

Al escribir esto en código, técnicamente podrías hacer

 public class Pizza() { public void Prepare(PizzaType tp) { switch (tp) { case PizzaType.StuffedCrust: // prepare stuffed crust ingredients in system break; case PizzaType.DeepDish: // prepare deep dish ingredients in system break; //.... etc. } } } 

Sin embargo, las pizzas de plato profundo (en términos de C #) pueden requerir que se establezcan propiedades diferentes en el método Prepare() que en la corteza rellena, y así termina con muchas propiedades opcionales, y la clase no se escala bien (¿qué pasa si agregas nuevos tipos de pizza).

La forma correcta de resolver esto es usar la interfaz. La interfaz declara que todas las pizzas se pueden preparar, pero cada pizza se puede preparar de forma diferente. Entonces, si tienes las siguientes interfaces:

 public interface IPizza { void Prepare(); } public class StuffedCrustPizza : IPizza { public void Prepare() { // Set settings in system for stuffed crust preparations } } public class DeepDishPizza : IPizza { public void Prepare() { // Set settings in system for deep dish preparations } } 

Ahora su código de manejo de pedidos no necesita saber exactamente qué tipos de pizzas se ordenaron para manejar los ingredientes. Simplemente tiene:

 public PreparePizzas(IList pizzas) { foreach (IPizza pizza in pizzas) pizza.Prepare(); } 

Aunque cada tipo de pizza se prepara de forma diferente, esta parte del código no tiene que importar qué tipo de pizza estamos tratando, simplemente sabe que se está pidiendo pizzas y, por lo tanto, cada llamada a Prepare preparará automáticamente cada pizza correctamente según su tipo, incluso si la colección tiene múltiples tipos de pizzas.

Para mí, el punto a estos solo se volvió claro cuando dejas de mirarlos para que tu código sea más fácil / rápido de escribir: este no es su propósito. Tienen una serie de usos:

(Esto va a perder la analogía de la pizza, ya que no es muy fácil visualizar el uso de esto)

Digamos que estás haciendo un juego simple en la pantalla y tendrá criaturas con las que interactúas.

R: Pueden hacer que su código sea más fácil de mantener en el futuro mediante la introducción de un acoplamiento flexible entre su interfaz y su implementación de back-end.

Podrías escribir esto para empezar, ya que solo habrá trolls:

 // This is our back-end implementation of a troll class Troll { void Walk(int distance) { //Implementation here } } 

Interfaz:

 function SpawnCreature() { Troll aTroll = new Troll(); aTroll.Walk(1); } 

Dos semanas después, el departamento de marketing decide que también necesita Orcos, ya que leen sobre ellos en Twitter, por lo que tendría que hacer algo como:

 class Orc { void Walk(int distance) { //Implementation (orcs are faster than trolls) } } 

Interfaz:

 void SpawnCreature(creatureType) { switch(creatureType) { case Orc: Orc anOrc = new Orc(); anORc.Walk(); case Troll: Troll aTroll = new Troll(); aTroll.Walk(); } } 

Y puedes ver cómo esto comienza a complicarse. Puede usar una interfaz aquí para que su parte delantera se escriba una vez y (este es el bit más importante) probado, y luego puede enchufar más elementos de la parte posterior según sea necesario:

 interface ICreature { void Walk(int distance) } public class Troll : ICreature public class Orc : ICreature //etc 

El front end es entonces:

 void SpawnCreature(creatureType) { ICreature creature; switch(creatureType) { case Orc: creature = new Orc(); case Troll: creature = new Troll(); } creature.Walk(); } 

El front end ahora solo se preocupa por la interfaz ICreature: no le molesta la implementación interna de un troll o un orco, sino solo por el hecho de que implementan ICreature.

Un punto importante a tener en cuenta al mirar esto desde este punto de vista es que también podría haber usado una clase de criatura abstracta, y desde esta perspectiva, tiene el mismo efecto.

Y podrías extraer la creación a una fábrica:

 public class CreatureFactory { public ICreature GetCreature(creatureType) { ICreature creature; switch(creatureType) { case Orc: creature = new Orc(); case Troll: creature = new Troll(); } return creature; } } 

Y nuestra interfaz se convertiría en:

 CreatureFactory _factory; void SpawnCreature(creatureType) { ICreature creature = _factory.GetCreature(creatureType); creature.Walk(); } 

El front end ahora ni siquiera tiene que tener una referencia a la biblioteca donde se implementan Troll y Orc (siempre que la fábrica esté en una biblioteca separada), no necesita saber nada de ellos en absoluto.

B: Supongamos que tiene una funcionalidad que solo algunas criaturas tendrán en su estructura de datos por lo demás homogénea , por ejemplo

 interface ICanTurnToStone { void TurnToStone(); } public class Troll: ICreature, ICanTurnToStone 

El front end podría ser:

 void SpawnCreatureInSunlight(creatureType) { ICreature creature; switch(creatureType) { case Orc: creature = new Orc(); case Troll: creature = new Troll(); } creature.Walk(); if (creature is ICanTurnToStone) { (ICanTurnToStone)creature.TurnToStone(); } } 

C: uso para la dependency injection

La mayoría de los marcos de dependency injection son más fáciles de usar cuando hay un acoplamiento muy flexible entre el código de la interfaz y la implementación de la parte final. Si tomamos nuestro ejemplo de fábrica de arriba y hacemos que nuestra fábrica implemente una interfaz:

 public interface ICreatureFactory { ICreature GetCreature(string creatureType); } 

Nuestro front-end podría tener esto inyectado (por ejemplo, un controlador MVC API) a través del constructor (típicamente):

 public class CreatureController : Controller { private readonly ICreatureFactory _factory; public CreatureController(ICreatureFactory factory) { _factory = factory; } public HttpResponseMessage TurnToStone(string creatureType) { ICreature creature = _factory.GetCreature(creatureType); creature.TurnToStone(); return Request.CreateResponse(HttpStatusCode.OK); } } 

Con nuestro marco DI (por ejemplo, Ninject o Autofac), podemos configurarlos para que, en tiempo de ejecución, se cree una instancia de CreatureFactory siempre que se necesite un ICreatureFactory en un constructor, lo que hace que nuestro código sea agradable y simple.

También significa que cuando escribimos una prueba unitaria para nuestro controlador, podemos proporcionar un ICreatureFactory burlado (por ejemplo, si la implementación concreta requiere acceso DB, no queremos que nuestras pruebas de unidad dependan de eso) y probar fácilmente el código en nuestro controlador .

D: Hay otros usos, por ejemplo, usted tiene dos proyectos A y B que por razones ‘legacy’ no están bien estructurados, y A tiene una referencia a B.

Luego encuentra la funcionalidad en B que necesita llamar a un método que ya está en A. No puede hacerlo usando implementaciones concretas a medida que obtiene una referencia circular.

Puede tener una interfaz declarada en B que la clase en A implementa. Su método en B puede pasar una instancia de una clase que implemente la interfaz sin problemas, aunque el objeto concreto sea de un tipo en A.

Aquí están sus ejemplos reexplicados:

 public interface IFood // not Pizza { public void Prepare(); } public class Pizza : IFood { public void Prepare() // Not order for explanations sake { //Prepare Pizza } } public class Burger : IFood { public void Prepare() { //Prepare Burger } } 

Los ejemplos anteriores no tienen mucho sentido. Podrías lograr todos los ejemplos anteriores usando clases (clase abstracta si quieres que se comporte solo como un contrato ):

 public abstract class Food { public abstract void Prepare(); } public class Pizza : Food { public override void Prepare() { /* Prepare pizza */ } } public class Burger : Food { public override void Prepare() { /* Prepare Burger */ } } 

Obtienes el mismo comportamiento que con la interfaz. Puede crear una List y repetirla sin saber qué clase se encuentra en la parte superior.

Un ejemplo más adecuado sería la herencia múltiple:

 public abstract class MenuItem { public string Name { get; set; } public abstract void BringToTable(); } // Notice Soda only inherits from MenuItem public class Soda : MenuItem { public override void BringToTable() { /* Bring soda to table */ } } // All food needs to be cooked (real food) so we add this // feature to all food menu items public interface IFood { void Cook(); } public class Pizza : MenuItem, IFood { public override void BringToTable() { /* Bring pizza to table */ } public void Cook() { /* Cook Pizza */ } } public class Burger : MenuItem, IFood { public override void BringToTable() { /* Bring burger to table */ } public void Cook() { /* Cook Burger */ } } 

Luego puede usarlos todos como MenuItem y no les importa cómo manejan cada llamada al método.

 public class Waiter { public void TakeOrder(IEnumerable order) { // Cook first // (all except soda because soda is not IFood) foreach (var food in order.OfType()) food.Cook(); // Bring them all to the table // (everything, including soda, pizza and burger because they're all menu items) foreach (var menuItem in order) menuItem.BringToTable(); } } 

Explicación simple con analogía

El problema a resolver: ¿Cuál es el propósito del polymorphism?

Analogía: entonces soy un jefe en un sitio de construcción.

Los comerciantes caminan en el sitio de construcción todo el tiempo. No sé quién va a atravesar esas puertas. Pero básicamente les digo qué hacer.

  1. Si es carpintero, digo: construye andamios de madera.
  2. Si es un plomero, digo: “Configure las tuberías”
  3. Si es un electricista, le digo: “Extraiga los cables y reemplácelos con cables de fibra óptica”.

El problema con el enfoque anterior es que tengo que: (i) saber quién está entrando por esa puerta, y dependiendo de quién sea, tengo que decirles qué hacer. Eso significa que tengo que saber todo sobre un comercio en particular. Hay costos / beneficios asociados con este enfoque:

Las implicaciones de saber qué hacer:

  • Esto significa que si el código del carpintero cambia de: BuildScaffolding() a BuildScaffold() (es decir, un ligero cambio de nombre), también tendré que cambiar la clase de llamada (es decir, la clase de Foreperson ) también – tendrá que hacer dos cambios al código en lugar de (básicamente) solo uno. Con el polymorphism, usted (básicamente) solo necesita hacer un cambio para lograr el mismo resultado.

  • En segundo lugar, no tendrá que preguntar constantemente: ¿quién es usted? ok haz esto … ¿quién eres tú? ok, eso … polymorphism – DRYs codifica y es muy efectivo en ciertas situaciones:

  • con polymorphism puede agregar fácilmente clases adicionales de comerciantes sin cambiar ningún código existente. (es decir, el segundo de los principios de diseño SÓLIDO: principio de cierre abierto).

La solución

Imagine un escenario en el que, sin importar quién entre a la puerta, pueda decir: “Trabajar ()” y hagan sus trabajos de respeto en los que se especialicen: el fontanero se ocuparía de las tuberías y el electricista se ocuparía de los cables.

El beneficio de este enfoque es que: (i) No necesito saber exactamente quién está entrando por esa puerta, todo lo que necesito saber es que serán un tipo de tradie y que pueden hacer el trabajo, y en segundo lugar , (ii) no necesito saber nada sobre ese comercio en particular. El tradie se encargará de eso.

Entonces en vez de esto:

 If(electrician) then electrician.FixCablesAndElectricity() if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

Puedo hacer algo como esto:

 ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment. tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!! 

¿Cuál es el beneficio?

El beneficio es que si los requisitos específicos del trabajo del carpintero cambian, entonces el capataz no necesitará cambiar su código, no necesita saber ni preocuparse. Todo lo que importa es que el carpintero sepa a qué se refiere Work (). En segundo lugar, si un nuevo tipo de trabajador de la construcción entra al sitio de trabajo, entonces el capataz no necesita saber nada sobre el oficio; lo único que le importa al capataz es si el trabajador de la construcción (por ejemplo, Soldador, Glazier, Solador, etc.) puede obtener un poco de trabajo () hecho.


Problema ilustrado y solución (con y sin interfaces):

Sin interfaz (Ejemplo 1):

Ejemplo 1: sin interfaz

Sin interfaz (Ejemplo 2):

Ejemplo 2: sin una interfaz

Con una interfaz:

Ejemplo 3: los beneficios de usar una interfaz

Resumen

Una interfaz le permite hacer que la persona haga el trabajo al que están asignados, sin que usted tenga el conocimiento exacto de quiénes son o los detalles de lo que pueden hacer. Esto le permite agregar fácilmente nuevos tipos (de comercio) sin cambiar su código existente (técnicamente, sí lo cambia un poquito), y ese es el beneficio real de un enfoque OOP frente a una metodología de progtwigción más funcional.

Si no comprende alguno de los anteriores o si no está claro, pregunte en un comentario e intentaré mejorar la respuesta.

En ausencia de tipado de pato, como puede usarlo en Python, C # depende de las interfaces para proporcionar abstracciones. Si las dependencias de una clase son todos tipos concretos, no se puede pasar de otro tipo, utilizando interfaces que se pueden pasar en cualquier tipo que implemente la interfaz.

El ejemplo de Pizza es malo porque debe usar una clase abstracta que maneja el pedido, y las pizzas deben anular el tipo de pizza, por ejemplo.

Usas interfaces cuando tienes una propiedad compartida, pero tus clases heredan de diferentes lugares, o cuando no tienes ningún código común que puedas usar. Por ejemplo, se usan cosas que pueden desecharse IDisposable , usted sabe que serán desechadas, simplemente no sabe qué sucederá cuando se eliminen.

Una interfaz es solo un contrato que le dice algunas cosas que un objeto puede hacer, qué parámetros y qué tipos de retorno esperar.

Considere el caso donde no controla ni posee las clases base.

Tome los controles visuales, por ejemplo, en .NET for Winforms todos heredan de la clase base Control, que está completamente definida en .NET Framework.

Supongamos que está en el negocio de crear controles personalizados. Desea crear nuevos botones, cuadros de texto, listas de vista, cuadrículas, etc. y le gustaría que todos tengan ciertas características exclusivas de su conjunto de controles.

Por ejemplo, es posible que desee una forma común de manejar el tema, o una forma común de manejar la localización.

En este caso, no puede “simplemente crear una clase base” porque si lo hace, debe volver a implementar todo lo relacionado con los controles.

En su lugar, descenderá de Button, TextBox, ListView, GridView, etc. y agregará su código.

Pero esto plantea un problema, ¿cómo puede identificar ahora qué controles son “suyos”, cómo puede construir un código que diga “para todos los controles en el formulario que son míos, establezca el tema en X”.

Ingrese las interfaces.

Las interfaces son una forma de mirar un objeto, para determinar que el objeto se adhiere a un determinado contrato.

Debería crear “YourButton”, descender de Button y agregar soporte para todas las interfaces que necesita.

Esto te permitiría escribir código como el siguiente:

 foreach (Control ctrl in Controls) { if (ctrl is IMyThemableControl) ((IMyThemableControl)ctrl).SetTheme(newTheme); } 

Esto no sería posible sin interfaces, en su lugar, tendría que escribir un código como este:

 foreach (Control ctrl in Controls) { if (ctrl is MyThemableButton) ((MyThemableButton)ctrl).SetTheme(newTheme); else if (ctrl is MyThemableTextBox) ((MyThemableTextBox)ctrl).SetTheme(newTheme); else if (ctrl is MyThemableGridView) ((MyThemableGridView)ctrl).SetTheme(newTheme); else .... } 

Considere que no puede usar herencia múltiple en C # y luego vuelva a analizar su pregunta.

En este caso, podría (y probablemente lo haría) simplemente definir una clase base de pizza y heredar de ellos. Sin embargo, hay dos razones por las que las interfaces le permiten hacer cosas que no se pueden lograr de otras maneras:

  1. Una clase puede implementar múltiples interfaces. Simplemente define las características que la clase debe tener. Implementar una gama de interfaces significa que una clase puede cumplir múltiples funciones en diferentes lugares.

  2. Una interfaz se puede definir en un ámbito más hogher que la clase o la persona que llama. Esto significa que puede separar la funcionalidad, separar la dependencia del proyecto y mantener la funcionalidad en un proyecto o clase, y la implementación de esto en otro lugar.

Una implicación de 2 es que puede cambiar la clase que se está utilizando, solo requiere que implemente la interfaz adecuada.

Interfaz = contrato, utilizado para acoplamiento flojo (ver GRASP ).

Si estoy trabajando en una API para dibujar formas, es posible que desee utilizar DirectX o llamadas gráficas, o OpenGL. Entonces, crearé una interfaz, que abstraerá mi implementación de lo que llamas.

Entonces llamas a un método de fábrica: MyInterface i = MyGraphics.getInstance() . Entonces, tienes un contrato, entonces sabes qué funciones puedes esperar en MyInterface . Por lo tanto, puede llamar a i.drawRectangle o i.drawCube y saber que si intercambia una biblioteca por otra, que las funciones son compatibles.

Esto se vuelve más importante si está usando Dependency Injection, ya que entonces, en un archivo XML, puede intercambiar implementaciones.

Por lo tanto, puede tener una biblioteca criptográfica que pueda exportarse para uso general y otra que esté a la venta solo para compañías estadounidenses, y la diferencia está en que usted cambia un archivo de configuración y el rest del progtwig no está disponible. cambiado

Esto se usa mucho con las colecciones en .NET, ya que solo debe usar, por ejemplo, las variables de List , y no se preocupe si se trata de una ArrayList o LinkedList.

Siempre que codifique en la interfaz, el desarrollador puede cambiar la implementación real y el rest del progtwig no se modificará.

Esto también es útil cuando se realizan pruebas unitarias, ya que puedes simular interfaces completas, así que no tengo que ir a una base de datos, sino a una implementación burlada que solo devuelve datos estáticos, así puedo probar mi método sin preocuparme si la base de datos está fuera de servicio por mantenimiento o no.

Una interfaz es realmente un contrato que las clases de implementación deben seguir, de hecho es la base para casi todos los patrones de diseño que conozco.

En su ejemplo, la interfaz se crea porque todo lo que IS A Pizza, lo que significa que implementa la interfaz Pizza, está garantizado para haber implementado

 public void Order(); 

Después de su código mencionado, podría tener algo como esto:

 public void orderMyPizza(IPizza myPizza) { //This will always work, because everyone MUST implement order myPizza.order(); } 

De esta forma estás usando el polymorphism y lo único que te importa es que tus objetos respondan al orden ().

Las interfaces son para aplicar la conexión entre diferentes clases. por ejemplo, tienes una clase para auto y un árbol;

 public class Car { ... } public class Tree { ... } 

desea agregar una funcionalidad consumible para ambas clases. Pero cada clase tiene sus propias formas de quemar. así que simplemente haces;

 public class Car : IBurnable { public void Burn() { ... } } public class Tree : IBurnable { public void Burn() { ... } } 

Obtendrás interfaces, cuando las necesites 🙂 Puedes estudiar ejemplos, ¡pero necesitas el Aha! efecto para realmente obtenerlos.

Ahora que sabes qué interfaces son, simplemente codifica sin ellas. Tarde o temprano te encontrarás con un problema, donde el uso de interfaces será lo más natural.

Me sorprende que no haya muchas publicaciones que contengan la razón más importante para una interfaz: Patrones de diseño . Es la imagen más grande en el uso de contratos, y aunque es una syntax que decora el código máquina (para ser honesto, el comstackdor probablemente los ignora), la abstracción y las interfaces son fundamentales para OOP, comprensión humana y architectures complejas de sistemas.

Ampliemos la analogía de la pizza para decir una comida completa de 3 platos. Todavía tendremos la interfaz Core Prepare() para todas nuestras categorías de alimentos, pero también tendremos declaraciones abstractas para las selecciones de cursos (starter, main, dessert) y las diferentes propiedades para los tipos de alimentos (savory / sweet, vegetarian / non -vegetariano, sin gluten, etc.).

Con base en estas especificaciones, podríamos implementar el patrón Abstract Factory para conceptualizar todo el proceso, pero usar interfaces para garantizar que solo los cimientos fueran concretos. Todo lo demás podría ser flexible o alentar el polymorphism, pero mantener la encapsulación entre las diferentes clases de Course que implementan la interfaz del ICourse .

Si tuviera más tiempo, me gustaría elaborar un ejemplo completo de esto, o alguien puede extender esto por mí, pero en resumen, una interfaz C # sería la mejor herramienta para diseñar este tipo de sistema.

Aquí hay una interfaz para objetos que tienen una forma rectangular:

 interface IRectangular { Int32 Width(); Int32 Height(); } 

Todo lo que exige es que implemente formas de acceder al ancho y alto del objeto.

Ahora definamos un método que funcionará en cualquier objeto que sea IRectangular :

 static class Utils { public static Int32 Area(IRectangular rect) { return rect.Width() * rect.Height(); } } 

Eso devolverá el área de cualquier objeto rectangular.

Implementemos una clase SwimmingPool que es rectangular:

 class SwimmingPool : IRectangular { int width; int height; public SwimmingPool(int w, int h) { width = w; height = h; } public int Width() { return width; } public int Height() { return height; } } 

Y otra House clase que también es rectangular:

 class House : IRectangular { int width; int height; public House(int w, int h) { width = w; height = h; } public int Width() { return width; } public int Height() { return height; } } 

Dado eso, puede llamar al método de Area en casas o piscinas:

 var house = new House(2, 3); var pool = new SwimmingPool(3, 4); Console.WriteLine(Utils.Area(house)); Console.WriteLine(Utils.Area(pool)); 

In this way, your classes can “inherit” behavior (static-methods) from any number of interfaces.

I did a search for the word “composition” on this page and didn’t see it once. This answer is very much in addition to the answers aforementioned.

One of the absolutely crucial reasons for using interfaces in an Object Oriented Project is that they allow you to favour composition over inheritance. By implementing interfaces you can decouple your implementations from the various algorithms you are applying to them.

This superb “Decorator Pattern” tutorial by Derek Banas (which – funnily enough – also uses pizza as an example) is a worthwhile illustration:

https://www.youtube.com/watch?v=j40kRwSm4VE

An interface defines a contract between the provider of a certain functionality and the correspondig consumers. It decouples the implementation from the contract (interface). You should have a look at object oriented architecture and design. You may want to start with wikipedia: http://en.wikipedia.org/wiki/Interface_(computing)

The main purpose of the interfaces is that it makes a contract between you and any other class that implement that interface which makes your code decoupled and allows expandability.

Therese are ask really great examples.

Another, in the case of a switch statement, you no longer have the need to maintain and switch every time you want rio perform a task in a specific way.

In your pizza example, if want to make a pizza, the interface is all you need, from there each pizza takes care of it’s own logic.

This helps to reduce coupling and cyclomatic complexity. You have to still implement the logic but there will be less you have to keep track of in the broader picture.

For each pizza you can then keep track of information specific to that pizza. What other pizzas have doesn’t matter because only the other pizzas need to know.

The simplest way to think about interfaces is to recognize what inheritance means. If class CC inherits class C, it means both that:

  1. Class CC can use any public or protected members of class C as though they were its own, and thus only needs to implement things which do not exist in the parent class.
  2. A reference to a CC can be passed or assigned to a routine or variable that expects a reference to a C.

Those two function of inheritance are in some sense independent; although inheritance applies both simultaneously, it is also possible to apply the second without the first. This is useful because allowing an object to inherit members from two or more unrelated classes is much more complicated than allowing one type of thing to be substitutable for multiple types.

An interface is somewhat like an abstract base class, but with a key difference: an object which inherits a base class cannot inherit any other class. By contrast, an object may implement an interface without affecting its ability to inherit any desired class or implement any other interfaces.

One nice feature of this (underutilized in the .net framework, IMHO) is that they make it possible to indicate declaratively the things an object can do. Some objects, for example, will want data-source object from which they can retrieve things by index (as is possible with a List), but they won’t need to store anything there. Other routines will need a data-depository object where they can store things not by index (as with Collection.Add), but they won’t need to read anything back. Some data types will allow access by index, but won’t allow writing; others will allow writing, but won’t allow access by index. Some, of course, will allow both.

If ReadableByIndex and Appendable were unrelated base classes, it would be impossible to define a type which could be passed both to things expecting a ReadableByIndex and things expecting an Appendable. One could try to mitigate this by having ReadableByIndex or Appendable derive from the other; the derived class would have to make available public members for both purposes, but warn that some public members might not actually work. Some of Microsoft’s classes and interfaces do that, but that’s rather icky. A cleaner approach is to have interfaces for the different purposes, and then have objects implement interfaces for the things they can actually do. If one had an interface IReadableByIndex and another interface IAppendable, classes which could do one or the other could implement the appropriate interfaces for the things they can do.

Interfaces can also be daisy chained to create yet another interface. This ability to implement multiple Interfaces give the developer the advantage of adding functionality to their classes without having to change current class functionality (SOLID Principles)

O = “Classes should be open for extension but closed for modification”

There are a lot of good answers here but I would like to try from a slightlt different perspective.

You may be familiar with the SOLID principles of object oriented design. En resumen:

S – Single Responsibility Principle O – Open/Closed Principle L – Liskov Substitution Principle I – Interface Segregation Principle D – Dependency Inversion Principle

Following the SOLID principles helps to produce code that is clean, well factored, cohesive and loosely coupled. Dado que:

“Dependency management is the key challenge in software at every scale” (Donald Knuth)

then anything that helps with dependency management is a big win. Interfaces and the Dependency Inversion Principle really help to decouple code from dependencies on concrete classes, so code can be written and reasoned about in terms of behaviours rather than implementations. This helps to break the code into components which can be composed at runtime rather than compile time and also means those components can be quite easily plugged in and out without having to alter the rest of the code.

Interfaces help in particular with the Dependency Inversion Principle, where code can be componentized into a collection of services, with each service being described by an interface. Services can then be “injected” into classes at runtime by passing them in as a constructor parameter. This technique really becomes critical if you start to write unit tests and use test driven development. ¡Intentalo! You will quickly understand how interfaces help to break apart the code into manageable chunks that can be individually tested in isolation.

To me an advantage/benefit of an interface is that it is more flexible than an abstract class. Since you can only inherit 1 abstract class but you can implement multiple interfaces, changes to a system that inherits an abstract class in many places becomes problematic. If it is inherited in 100 places, a change requires changes to all 100. But, with the interface, you can place the new change in a new interface and just use that interface where its needed (Interface Seq. from SOLID). Additionally, the memory usage seems like it would be less with the interface as an object in the interface example is used just once in memory despite how many places implement the interface.

Am I correct then to take another look at Interfaces, The graphical interface (winforms / WPF) is like the Interface. It displays only what the end user will interact with. The end user will then not have to know what went into the design of the application, but would know what they can do with it, based on available options on the form. In OOP view then the idea is to create a structured interface that informs other users of your eg DLL libraries what is available to use and that it’s like an guarantee/contract that the field, methods and properties will be available to use (inherit in your classes).

I share your sense that Interfaces are not necessary. Here is a quote from Cwalina pg 80 Framework Design Guidelines “I often here people saying that interfaces specify contracts. I believe this a dangerous myth. Interfaces by themselves do not specify much. …” He and co-author Abrams managed 3 releases of .Net for Microsoft. He goes on to say that the ‘contract’ is “expressed” in an implementation of the class. IMHO watching this for decades, there were many people warning Microsoft that taking the engineering paradigm to the max in OLE/COM might seem good but its usefulness is more directly to hardware. Especially in a big way in the 80s and 90s getting interoperating standards codified. In our TCP/IP Internet world there is little appreciation of the hardware and software gymnastics we would jump through to get solutions ‘wired up’ between and among mainframes, minicomputers, and microprocessors of which PCs were just a small minority. So coding to interfaces and their protocols made computing work. And interfaces ruled. But what does solving making X.25 work with your application have in common with posting recipes for the holidays? I have been coding C++ and C# for many years and I never created one once.