oop - porque - polimorfismo java
¿Por qué querría usar interfaces? (20)
Aquí hay varias respuestas más a una pregunta similar: interfaz vs clase base
Entiendo que te obligan a implementar métodos, pero lo que no puedo entender es por qué querrías usarlos. ¿Alguien puede darme un buen ejemplo o una explicación sobre por qué querría implementar esto?
Como ha señalado, las interfaces son útiles cuando quiere obligar a alguien a hacerlo en un formato determinado.
Las interfaces son buenas cuando los datos que no están en un formato determinado pueden significar suposiciones peligrosas en el código.
Por ejemplo, en este momento estoy escribiendo una aplicación que transformará datos de un formato a otro. Quiero obligarlos a ubicar esos campos, así sé que existirán y que tendrán mayores posibilidades de implementarse correctamente. No me importa si sale otra versión y no compila para ellos porque es más probable que se necesiten datos de todos modos.
Las interfaces rara vez se utilizan debido a esto, ya que generalmente puede hacer suposiciones o realmente no necesita los datos para hacer lo que necesita hacer.
Creo que debes entender bien los patrones de diseño, así que observa el poder.
Echa un vistazo a los patrones de diseño de Head First
Digamos que quieres hacer un seguimiento de una colección de cosas. Dichas colecciones deben admitir un montón de cosas, como agregar y eliminar elementos, y verificar si un artículo está en la colección.
A continuación, puede especificar una interfaz ICollection con los métodos add (), remove () y contains ().
Código que no necesita saber qué tipo de colección (Lista, Array, Hash-table, Árbol rojo-negro, etc.) podría aceptar objetos que implementaban la interfaz y trabajar con ellos sin conocer su tipo real.
El mejor código de Java que he visto definió casi todas las referencias de objetos como instancias de interfaces en lugar de instancias de clases. Es una fuerte señal de código de calidad diseñado para la flexibilidad y el cambio.
En .Net, creo clases base y heredo de ellas cuando las clases están de alguna manera relacionadas. Por ejemplo, la Persona de la clase base podría ser heredada por Empleado y Cliente. La persona puede tener propiedades comunes, como campos de direcciones, nombre, teléfono, etc. El empleado puede tener su propia propiedad del departamento. El cliente tiene otras propiedades exclusivas.
Dado que una clase solo puede heredar de otra clase en .Net, uso interfaces para funcionalidad compartida adicional. Algunas veces las interfaces son compartidas por clases que de otro modo no están relacionadas. El uso de una interfaz crea un contrato que los desarrolladores sabrán que es compartido por todas las otras clases que lo implementan. También forzo esas clases para implementar a todos sus miembros.
En las interfaces C # también son extremadamente útiles para permitir el polimorfismo para las clases que no comparten las mismas clases base. Es decir, dado que no podemos tener herencia múltiple, puede usar interfaces para permitir el uso de diferentes tipos. También es una forma de permitirle exponer miembros privados para su uso sin reflexión (implementación explícita), por lo que puede ser una buena forma de implementar la funcionalidad mientras mantiene limpio su modelo de objetos.
Por ejemplo:
public interface IExample
{
void Foo();
}
public class Example : IExample
{
// explicit implementation syntax
void IExample.Foo() { ... }
}
/* Usage */
Example e = new Example();
e.Foo(); // error, Foo does not exist
((IExample)e).Foo(); // success
En un artículo en mi blog, describo brevemente los tres propósitos que tienen las interfaces.
Las interfaces pueden tener diferentes propósitos:
- Proporcione implementaciones diferentes para el mismo objetivo. El ejemplo típico es una lista, que puede tener implementaciones diferentes para diferentes casos de uso de rendimiento (LinkedList, ArrayList, etc.).
- Permitir modificación de criterios. Por ejemplo, una función de clasificación puede aceptar una interfaz Comparable para proporcionar cualquier tipo de criterio de clasificación, basado en el mismo algoritmo.
- Ocultar detalles de implementación. Esto también hace que sea más fácil para un usuario leer los comentarios, ya que en el cuerpo de la interfaz solo hay métodos, campos y comentarios, sin fragmentos largos de código para omitir.
Aquí está el texto completo del artículo: http://weblogs.manas.com.ar/ary/2007/11/
Hay una serie de razones para hacerlo. Cuando usa una interfaz, está listo en el futuro cuando necesita refactorizar / reescribir el código. También puede proporcionar una especie de API estandarizada para operaciones simples.
Por ejemplo, si desea escribir un algoritmo de ordenamiento como el quicksort, todo lo que necesita para ordenar cualquier lista de objetos es que pueda comparar con éxito dos de los objetos. Si crea una interfaz, digamos ISortable, cualquiera que cree objetos puede implementar la interfaz ISortable y pueden usar su código de ordenación.
Si está escribiendo código que usa un almacenamiento de base de datos y escribe en una interfaz de almacenamiento, puede reemplazar ese código en la línea.
Las interfaces fomentan un acoplamiento más flexible de su código para que pueda tener una mayor flexibilidad.
Imagine la siguiente interfaz básica que define un mecanismo CRUD básico:
interface Storable {
function create($data);
function read($id);
function update($data, $id);
function delete($id);
}
Desde esta interfaz, puede ver que cualquier objeto que la implemente debe tener funcionalidad para crear, leer, actualizar y eliminar datos. Esto podría hacerse mediante una conexión de base de datos, un lector de archivos CSV y un lector de archivos XML, o cualquier otro tipo de mecanismo que pueda querer usar operaciones CRUD.
Por lo tanto, ahora podría tener algo como lo siguiente:
class Logger {
Storable storage;
function Logger(Storable storage) {
this.storage = storage;
}
function writeLogEntry() {
this.storage.create("I am a log entry");
}
}
A este registrador no le importa si transfiere una conexión de base de datos, o algo que manipule archivos en el disco. Todo lo que necesita saber es que puede llamar a create () y funcionará como se espera.
La siguiente pregunta que surge de esto es que, si las bases de datos y los archivos CSV, etc., pueden almacenar datos, ¿no deberían heredarse de un objeto almacenable genérico y así eliminar la necesidad de interfaces? La respuesta a esto es que no ... no todas las conexiones de bases de datos pueden implementar operaciones CRUD, y lo mismo se aplica a todos los lectores de archivos.
Las interfaces definen lo que el objeto es capaz de hacer y cómo debe usarlo ... ¡no lo que es!
La forma más fácil de entender las interfaces es que permiten que diferentes objetos expongan la funcionalidad COMÚN. Esto le permite al programador escribir un código mucho más simple y corto que los programas a una interfaz, entonces, siempre y cuando los objetos implementen esa interfaz, funcionará.
Ejemplo 1: hay muchos proveedores de bases de datos diferentes, MySQL, MSSQL, Oracle, etc. Sin embargo, todos los objetos de la base de datos pueden HACER las mismas cosas, por lo que encontrará muchas interfaces para los objetos de la base de datos. Si un objeto implementa IDBConnection, entonces expone los métodos Open () y Close (). Entonces, si quiero que mi programa sea independiente de la base de datos, programo en la interfaz y no a los proveedores específicos.
IDbConnection connection = GetDatabaseConnectionFromConfig()
connection.Open()
// do stuff
connection.Close()
Ver programando en una interfaz (IDbconnection) Ahora puedo SWAP cualquier proveedor de datos en mi configuración pero mi código permanece exactamente igual. Esta flexibilidad puede ser extremadamente útil y fácil de mantener. La desventaja de esto es que solo puedo realizar operaciones de base de datos ''genéricas'' y es posible que no utilice por completo la fortaleza que ofrece cada proveedor en particular, así como con todo en la programación tiene una compensación y debe determinar qué escenario lo beneficiará más.
Ejemplo 2: si observa casi todas las colecciones, implemente esta interfaz llamada IEnumerable. IEnumerable devuelve un IEnumerator que tiene MoveNext (), Current y Reset (). Esto permite que C # se mueva fácilmente a través de su colección. La razón por la que puede hacer esto es porque expone la interfaz IEnumerable. SABE que el objeto expone los métodos que necesita para atravesarlo. Esto hace dos cosas. 1) los bucles foreach ahora sabrán cómo enumerar la colección y 2) ahora puede aplicar poderosas exprsiones LINQ a su colección. De nuevo, la razón por la cual las interfaces son tan útiles aquí es porque todas las colecciones tienen algo en COMÚN, pueden moverse a través de ellas. Cada colección se puede mover de una manera diferente (lista vinculada vs matriz) pero esa es la belleza de las interfaces, es que la implementación está oculta e irrelevante para el consumidor de la interfaz. MoveNext () le proporciona el siguiente elemento de la colección, no importa cómo lo hace. Muy bien, ¿eh?
Ejemplo 3: cuando diseñe sus propias interfaces, solo tiene que hacerse una pregunta. ¿Qué tienen estas cosas en común? Una vez que encuentre todas las cosas que comparten los objetos, abstraiga esas propiedades / métodos en una interfaz para que cada objeto pueda heredar de ella. Entonces puede programar contra varios objetos usando una interfaz.
Y, por supuesto, tengo que dar mi ejemplo polimórfico de C ++ favorito, el ejemplo de los animales. Todos los animales comparten ciertas características. Digamos que pueden Mover, Hablar y todos tienen un Nombre. Como acabo de identificar lo que todos mis animales tienen en común y puedo abstraer esas cualidades en la interfaz IAnimal. Luego creo un objeto Bear, un objeto Owl y un objeto Snake que implementan esta interfaz. La razón por la que puede almacenar juntos diferentes objetos que implementan la misma interfaz es porque las interfaces representan una reasignación IS-A. Un oso es un animal IS-A, un búho ES-Un animal, por lo que hace que pueda recogerlos a todos como animales.
var animals = new IAnimal[] = {new Bear(), new Owl(), new Snake()} // here I can collect different objects in a single collection because they inherit from the same interface
foreach (IAnimal animal in animals)
{
Console.WriteLine(animal.Name)
animal.Speak() // a bear growls, a owl hoots, and a snake hisses
animal.Move() // bear runs, owl flys, snake slithers
}
Puedes ver que aunque estos animales realizan cada acción de una manera diferente, puedo programar contra todos ellos en un modelo unificado y este es solo uno de los muchos beneficios de las interfaces.
Entonces, nuevamente, lo más importante con las interfaces es qué tienen los objetos en común para que pueda programar contra DIFERENTES objetos de la MISMA manera. Ahorra tiempo, crea aplicaciones más flexibles, oculta la complejidad / implementación, modela objetos / situaciones del mundo real, entre muchos otros beneficios.
Espero que esto ayude.
Las interfaces definen los contratos , y esa es la palabra clave.
Utiliza una interfaz cuando necesita definir un contrato en su programa pero realmente no le importa el resto de las propiedades de la clase que cumple ese contrato siempre que lo haga.
Entonces, veamos un ejemplo. Supongamos que tiene un método que proporciona la funcionalidad para ordenar una lista. Lo primero ... ¿qué es una lista? ¿Realmente te importa qué elementos contiene para ordenar la lista? Su respuesta debería ser no ... En .NET (por ejemplo) usted tiene una interfaz llamada IList que define las operaciones que UNA lista DEBE soportar para que no le importen los detalles reales debajo de la superficie.
Volviendo al ejemplo, realmente no conoces la clase de los objetos en la lista ... ni a ti te importa. Si puedes comparar el objeto, también puedes ordenarlos. Entonces declaras un contrato:
interface IComparable
{
// Return -1 if this is less than CompareWith
// Return 0 if object are equal
// Return 1 if CompareWith is less than this
int Compare(object CompareWith);
}
ese contrato especifica que un método que acepta un objeto y devuelve un int debe implementarse para que sea comparable. Ahora ha definido un contrato y, por el momento, no le importa el objeto en sí, sino el contrato, por lo que puede hacer:
IComparable comp1 = list.GetItem(i) as IComparable;
if (comp1.Compare(list.GetItem(i+1)) < 0)
swapItem(list,i, i+1)
PD: Sé que los ejemplos son un poco ingenuos, pero son ejemplos ...
Las interfaces son absolutamente necesarias en un sistema orientado a objetos que espera hacer un buen uso del polimorfismo.
Un ejemplo clásico podría ser IVehicle, que tiene un método Move (). Podrías tener clases Coche, Bici y Tanque, que implementen IVehicle. Todos pueden moverse (), y podría escribir código que no le importaba con qué tipo de vehículo estaba tratando, solo para que pueda moverse ().
void MoveAVehicle(IVehicle vehicle)
{
vehicle.Move();
}
Las interfaces son una forma de polimorfismo. Un ejemplo:
Supongamos que quiere escribir algún código de registro. El registro irá a algún lugar (tal vez a un archivo, o un puerto serie en el dispositivo en el que se ejecuta el código principal, oa un socket, o que se descarta como / dev / null). No sabe dónde: el usuario de su código de registro debe ser libre de determinarlo. De hecho, a su código de registro no le importa. Solo quiere algo a lo que pueda escribir bytes.
Entonces, inventa una interfaz llamada "algo a lo que puede escribir bytes". Al código de registro se le da una instancia de esta interfaz (tal vez en tiempo de ejecución, tal vez esté configurado en tiempo de compilación. Todavía es polimorfismo, solo diferentes tipos). Usted escribe una o más clases que implementan la interfaz, y puede cambiar fácilmente dónde va el registro simplemente cambiando cuál usará el código de registro. Alguien más puede cambiar el lugar donde se registra escribiendo sus propias implementaciones de la interfaz, sin cambiar el código. Eso es básicamente lo que equivale al polimorfismo: saber lo suficiente sobre un objeto para usarlo de una manera particular, al tiempo que le permite variar en todos los aspectos que no necesita saber. Una interfaz describe cosas que necesita saber.
Los descriptores de archivos de C son básicamente una interfaz "algo que puedo leer y / o escribir bytes desde y / o a", y casi todos los lenguajes tipeados tienen tales interfaces al acecho en sus bibliotecas estándar: streams o lo que sea. Los idiomas sin par suelen tener tipos informales (quizás llamados contratos) que representan flujos. Así que, en la práctica, casi nunca tiene que inventarse realmente esta interfaz en particular: utiliza lo que el lenguaje le ofrece.
El registro y las transmisiones son solo un ejemplo: las interfaces ocurren cada vez que se puede describir en términos abstractos lo que se supone que debe hacer un objeto, pero no quieren vincularlo a una implementación / clase / cosa en particular.
Los pedales de un automóvil implementan una interfaz. Soy de los EE. UU. En donde manejamos en el lado derecho de la carretera. Nuestros volantes están en el lado izquierdo del automóvil. Los pedales para una transmisión manual de izquierda a derecha son embrague -> freno -> acelerador. Cuando fui a Irlanda, la conducción se invierte. Los volantes de los automóviles están a la derecha y conducen en el lado izquierdo de la carretera ... pero los pedales, ah los pedales ... implementaron la misma interfaz ... los tres pedales estaban en el mismo orden ... así que incluso si la clase era diferente y la red en la que funcionaba la clase era diferente, todavía me sentía cómodo con la interfaz del pedal. Mi cerebro fue capaz de llamar mis músculos en este auto como cualquier otro automóvil.
Piense en las numerosas interfaces sin programación sin las que no podemos vivir. Luego responde tu propia pregunta.
Primero, te dan una capa adicional de abstracción. Puede decir "Para esta función, este parámetro debe ser un objeto que tenga estos métodos con estos parámetros". Y probablemente también desee establecer el significado de estos métodos, en términos resumidos, pero que le permitan razonar sobre el código. En los lenguajes de pato lo obtienes de forma gratuita. No hay necesidad de "interfaces" de sintaxis explícitas. Sin embargo, probablemente todavía cree un conjunto de interfaces conceptuales, algo así como contratos (como en Design by Contract).
Además, las interfaces a veces se utilizan para fines menos "puros". En Java, se pueden usar para emular herencia múltiple. En C ++, puede usarlos para reducir los tiempos de compilación.
En general, reducen el acoplamiento en su código. Eso es bueno.
Su código también puede ser más fácil de probar de esta manera.
Un ejemplo típico es una arquitectura de complemento. El desarrollador A escribe la aplicación principal y quiere asegurarse de que todos los complementos escritos por el desarrollador B, C y D se ajusten a lo que su aplicación espera de ellos.
Una interfaz, define simplemente la interfaz . Más tarde, puede definir el método (en otras clases), que acepta interfaces como parámetros (o más exactamente, objeto que implementa esa interfaz). De esta forma, su método puede operar en una gran variedad de objetos, cuya única característica común es que implementan esa interfaz.
Un ejemplo específico: las interfaces son una buena forma de especificar un contrato que el código de otras personas debe cumplir.
Si estoy escribiendo una biblioteca de código, puedo escribir código que sea válido para objetos que tienen un cierto conjunto de comportamientos. La mejor solución es especificar esos comportamientos en una interfaz (sin implementación, solo una descripción) y luego usar referencias a los objetos que implementan esa interfaz en mi código de biblioteca.
Entonces puede aparecer cualquier persona al azar, crear una clase que implemente esa interfaz, instanciar un objeto de esa clase y pasarlo a mi código de biblioteca y esperar que funcione. Nota: por supuesto, es posible implementar una interfaz estrictamente ignorando la intención de la interfaz, por lo que simplemente implementar una interfaz no es garantía de que las cosas funcionen. ¡Estúpido siempre encuentra la manera! :-)
Otro ejemplo específico: dos equipos trabajando en diferentes componentes que deben cooperar. Si los dos equipos se sientan el primer día y acuerdan un conjunto de interfaces, entonces pueden ir por caminos separados e implementar sus componentes alrededor de esas interfaces. El Equipo A puede construir arneses de prueba que simulan el componente del Equipo B para la prueba, y viceversa. Desarrollo paralelo y menos errores.
El punto clave es que las interfaces proporcionan una capa de abstracción para que pueda escribir código que ignora detalles innecesarios.
El ejemplo canónico utilizado en la mayoría de los libros de texto es el de las rutinas de clasificación. Puede ordenar cualquier clase de objetos siempre que tenga una forma de comparar dos objetos cualquiera. Puede hacer que cualquier clase se pueda ordenar implementando la interfaz IComparable
, lo que le obliga a implementar un método para comparar dos instancias. Todas las rutinas de ordenación están escritas para manejar referencias a objetos IComparable, por lo que tan pronto como implemente IComparable puede usar cualquiera de esas rutinas de ordenamiento en colecciones de objetos de su clase.
When you need different classes to share same methods you use Interfaces.