oop - poo - ejercicios resueltos de clases abstractas en java
¿Debería cada objeto tener una interfaz y todos los objetos unidos libremente? (7)
Es útil para objetos que realmente proporcionan un servicio: autenticación, almacenamiento, etc. Para tipos simples que no tienen más dependencias y donde nunca habrá implementaciones alternativas, creo que está bien usar los tipos concretos.
Si te excedes con este tipo de cosas, terminas gastando mucho tiempo burlándote / anulando todo en el mundo, lo que a menudo puede terminar creando pruebas frágiles.
Según lo que he leído, la mejor práctica es tener clases basadas en una interfaz y acoplar los objetos de forma flexible, para ayudar a la reutilización del código y la prueba de la unidad.
¿Es correcto y es una regla que siempre se debe seguir?
La razón por la que pregunto es que recientemente trabajé en un sistema con cientos de objetos muy diferentes. Algunas interfaces compartidas comunes, pero la mayoría no, ¿y se preguntan si debería haber tenido una interfaz que refleje cada propiedad y función en esas clases?
Estoy usando C # y dot net 2.0; sin embargo, creo que esta pregunta podría adaptarse a muchos idiomas.
Estoy de acuerdo con kpollock. Las interfaces se utilizan para obtener un terreno común para los objetos. El hecho de que puedan ser utilizados en contenedores IOC y otros fines es una característica adicional.
Supongamos que tiene varios tipos de clases de clientes que varían ligeramente pero que tienen propiedades comunes. En este caso, es genial tener una interfaz ICustomer
para unirlos, lógicamente. Al hacerlo, podría crear una clase / método CustomerHander que maneje los objetos de ICholderer de la misma manera en lugar de crear un método de handerl para cada variación de clientes.
Esta es la fuerza de las interfaces. Si solo tiene una única clase que implementa una interfaz, entonces la interfaz no es de mucha ayuda, simplemente se sienta allí y no hace nada.
Las interfaces deben usarse cuando desee definir claramente la interacción entre dos secciones diferentes de su software. Especialmente cuando es posible que quiera arrancar cualquier extremo de la conexión y reemplazarlo por otra cosa.
Por ejemplo, en mi aplicación CAM tengo un CuttingPath conectado a una Colección de Puntos. No tiene sentido tener una interfaz IPointList ya que CuttingPaths siempre estará compuesto por puntos en mi aplicación.
Sin embargo, utilizo la interfaz IMotionController para comunicarme con la máquina porque soportamos muchos tipos diferentes de máquinas de corte, cada una con su propio conjunto de recomendaciones y métodos de comunicación. Entonces, en ese caso, tiene sentido colocarlo detrás de una interfaz, ya que una instalación puede estar usando una máquina diferente a otra.
Nuestras aplicaciones se han mantenido desde mediados de los años 80 y fuimos a un diseño orientado a objetos a finales de los 90. He descubierto que lo que podría cambiar excedió en gran medida lo que originalmente pensé y que el uso de las interfaces ha crecido. Por ejemplo, solía ser que nuestro DrawingPath estaba compuesto por puntos. Pero ahora está compuesto por entidades (splines, arcs, ec). Por lo tanto, se apunta a una EntityList que es una colección de Object que implementa la interfaz IEntity.
Pero ese cambio fue impulsado por la comprensión de que se podía dibujar un DrawingPath usando muchos métodos diferentes. Una vez que se descubrió que se necesitaba una variedad de métodos de dibujo, se indicó la necesidad de una interfaz en lugar de una relación fija con un objeto de entidad.
Tenga en cuenta que en nuestro sistema Drawing Paths se procesa en una ruta de corte de bajo nivel, que siempre es una serie de segmentos de puntos.
Realmente no. Los componentes de servicio (clase que hace cosas para su aplicación) son una buena opción para las interfaces, pero como regla, no me molestaría tener interfaces para, por ejemplo, clases de entidades básicas.
Por ejemplo: si está trabajando en un modelo de dominio, entonces ese modelo no debería ser interfaces. Sin embargo, si ese modelo de dominio desea llamar a clases de servicio (como acceso a datos, funciones del sistema operativo, etc.), entonces debería buscar interfaces para esos componentes. Esto reduce el acoplamiento entre las clases y significa que es la interfaz, o "contrato" lo que está acoplado.
En esta situación, a continuación, comienza a encontrar mucho más fácil escribir pruebas unitarias (porque puede tener stubs / mocks / fakes para el acceso a la base de datos, etc.) y puede usar IoC para intercambiar componentes sin recompilar las aplicaciones.
Solo usaría interfaces donde se requiriera ese nivel de abstracción, es decir, necesitas usar un comportamiento polimórfico. Los ejemplos más comunes serían la inyección de dependencias o si tiene un escenario tipo fábrica en algún lugar, o necesita establecer un comportamiento de tipo "herencia múltiple".
En mi caso, con mi estilo de desarrollo, esto es muy a menudo (estoy a favor de la agregación sobre las jerarquías de herencia profundas para la mayoría de las cosas que no sean controles de UI), pero he visto aplicaciones perfectamente finas que usan muy poco. Todo depende...
Ah, sí, y si interfiere con las interfaces, tenga cuidado con los servicios web. Si necesita exponer sus métodos de objetos a través de un servicio web, realmente no pueden devolver o tomar tipos de interfaz, solo tipos concretos (a menos que vaya a escribir a mano toda su serialización / deserialización). Sí, eso me ha mordido a lo grande ...
Una desventaja de la interfaz es que no pueden ser versionadas. Una vez que haya enviado la interfaz, no hará cambios en ella. Si usa clases abstractas, puede ampliar fácilmente el contrato a lo largo del tiempo agregando nuevos métodos y marcándolos como virtuales.
Como ejemplo, todos los objetos de transmisión en .NET derivan de System.IO.Stream que es una clase abstracta. Esto facilita a Microsoft agregar nuevas características. En la versión 2 de la infraestructura, agregaron las propiedades ReadTimeout y WriteTimeout sin romper ningún código. Si usaran una interfaz (digamos IStream), entonces no habrían podido hacer esto. En su lugar, tendrían que crear una nueva interfaz para definir los métodos de tiempo de espera y tendríamos que escribir el código para emitir condicionalmente esta interfaz si queríamos usar la funcionalidad.
Traté de tomar el consejo de ''código para una interfaz'' literalmente en un proyecto reciente. El resultado final fue esencialmente la duplicación de la interfaz pública (pequeña i) de cada clase, precisamente una vez en una implementación de interfaz (gran I). Esto es bastante inútil en la práctica.
Una mejor estrategia que creo es limitar tus implementaciones de interfaz a los verbos :
Print()
Draw()
Save()
Serialize()
Update()
... etc etc. Esto significa que las clases cuya función principal es almacenar datos, y si su código está bien diseñado, normalmente solo lo hacen, no desean o necesitan implementaciones de interfaz. En cualquier lugar, es posible que desee un comportamiento configurable en el tiempo de ejecución, por ejemplo, una variedad de estilos de gráficos diferentes que representen los mismos datos.
Es mejor aún cuando lo que pide el trabajo realmente no quiere saber cómo se hace el trabajo. Esto significa que puede darle un macguffin en el que simplemente confíe hará lo que su interfaz pública dice que hace, y dejará que el componente en cuestión simplemente elija cuándo hacer el trabajo.