multiple backbone .net design interface extension-methods abstract-class

.net - backbone - typescript multiple inheritance



Interface+Extension(mixin) vs Base Class (3)

Desventaja de los métodos de extensión: los clientes pre-C # 3 / VB9 no podrán usarlo tan fácilmente.

Por lo que a mí respecta, creo que el enfoque basado en la interfaz es mucho más agradable. A continuación, puede burlarse de sus dependencias muy bien, y todo está básicamente menos acoplado. No soy un gran admirador de la herencia de clase a menos que realmente se trate de especialización :)

EDITAR: Acabo de pensar en otro beneficio que podría ser relevante. Es posible que algunas de las implementaciones concretas puedan proporcionar versiones más optimizadas de algunos de los métodos generales.

Enumerable.Count es un buen ejemplo de esto: comprueba explícitamente si la secuencia implementa IList o no, porque si lo hace puede llamar a Count en la lista en lugar de iterar a través de toda la secuencia. Si IEnumerable<T> hubiera sido una clase abstracta con un método virtual Count() , podría haber sido reemplazado en List<T> lugar de que haya una implementación única que sepa sobre IList explícitamente. No estoy diciendo que esto siempre sea relevante, ni que IEnumerable<T> debería haber sido una clase abstracta (¡definitivamente no!), Simplemente señalándolo como una pequeña desventaja posible. Ahí es donde el polimorfismo realmente sería apropiado, especializando el comportamiento existente (admitiéndolo de una manera que solo afecta el rendimiento en lugar del resultado).

¿Es preferible una interfaz + métodos de extensión (mixin) a una clase abstracta?

Si su respuesta es "depende", ¿de qué depende?

Veo dos posibles ventajas para el enfoque de interfaz + extensión.

  • Las interfaces son heredables y las clases no.
  • Puede usar métodos de extensión para extender las interfaces de una manera ininterrumpida. (Los clientes que implementan su interfaz obtendrán su nueva implementación base pero aún podrán anularla).

Todavía no he pensado en un inconveniente de este enfoque. Puede haber una razón evidentemente simple por la cual el enfoque interfaz + extensión fallará.

Dos artículos útiles sobre este tema son


En mi humilde opinión, esta es la pregunta incorrecta.

Deberías usar todo para lo que está diseñado.

  • Los métodos de extensión no son miembros. Se parecen sintácticamente a miembros, por lo que puede encontrarlos más fácilmente.
  • Los métodos de extensión solo pueden usar la interfaz pública (o interna). Muchas otras clases podrían hacer lo mismo. Entonces, los métodos de extensión no son una verdadera encapsulación de una sola manera.
  • Son métodos estáticos y no pueden anularse y no se pueden burlar en una prueba unitaria. Es una característica de idioma no OO y la persona que llama está vinculada estáticamente a ella.

  • Las clases base abstractas a menudo se usan mal para "reutilizar el código" (en lugar de una herencia real). Esto se aplica a la herencia en general.

La pregunta debería ser: "¿Cuándo debería usar interfaces, métodos de extensión o clases base?"

  • use interfaces cada vez que necesite un contrato (y esto sucede todo el tiempo).
  • Usa clases base (abstractas) cuando tengas una situación de herencia real (podrías escribir un libro sobre cómo juzgar eso, así que simplemente lo dejo así). Una interfaz también se implementa principalmente al mismo tiempo.
  • utilice métodos de extensión para la lógica que no deberían ser realmente miembros del tipo, porque no es responsabilidad del tipo implementarlo, pero desea facilitar la búsqueda y se siente natural llamarlo miembro.

Editar:

O la pregunta debería ser: "¿Cómo escribo la funcionalidad reutilizable que no pertenece a una clase base?"

  • escribe una interfaz que expone la funcionalidad
  • escribir una clase de biblioteca reutilizable que implemente la funcionalidad
  • escriba una clase que implemente la interfaz y reutilice la funcionalidad al agregar la clase reutilizable.

En general, diría que los métodos de extensión son el lugar equivocado para la lógica comercial, excepto en casos especiales o decisiones especiales de diseño.

Las clases base son, en casos excepcionales, la decisión correcta. En duda, no lo es. Sin duda, deberías pensarlo nuevamente.


Las interfaces tienden a hacer que el código sea un poco más limpio. Creo que es un poco más fácil de probar. Cuando agrega Extensiones, agrega aún más flexibilidad mientras mantiene un código comprobable limpio.

Para mí, las clases abstractas siempre me han parecido torpes, usando interfaces puedo tener una fábrica de objetos que devuelve un objeto que es específico para lo que estoy tratando de lograr (separación de preocupaciones).

Simplemente inventando algo A tienen la interfaz llamada Math que tiene agregar, restar, dividir y multiplicar y luego tengo una clase llamada IntMAth que implementa Matemáticas que está optimizada para matemáticas enteras, y tengo otra clase llamada FloatMath que implementa las matemáticas que está optimizada. para Floating Math, y tengo un generalMath que implementa Math que maneja todo lo demás.

Cuando necesito agregar algunas carrozas, podría llamar a mi fábrica MathFactory.getMath (typeof (float)) y tiene cierta lógica para saber que si el tipo que paso es un flotante, entonces devuelve la clase FloatMath.

De esta forma, todas mis clases son más pequeñas y más fáciles de mantener, el código que llama a las clases es más pequeño, etc.