Tengo una clase con alguna funcionalidad predeterminada / compartida. Yo uso abstract class
para ello:
public interface ITypeNameMapper
{
string Map(TypeDefinition typeDefinition);
}
public abstract class TypeNameMapper : ITypeNameMapper
{
public virtual string Map(TypeDefinition typeDefinition)
{
if (typeDefinition is ClassDefinition classDefinition)
{
return Map(classDefinition);
}
...
throw new ArgumentOutOfRangeException(nameof(typeDefinition));
}
protected abstract string Map(ClassDefinition classDefinition);
}
Como puede ver, también tengo la interfaz ITypeNameMapper
. ¿Tiene sentido definir esta interfaz si ya tengo una clase abstracta TypeNameMapper
o si abstract class
es suficiente?
TypeDefinition
en este ejemplo mínimo también es abstracto.
c#
interfaces
abstract-class
Konrad
fuente
fuente
Respuestas:
Sí, porque C # no permite la herencia múltiple, excepto con interfaces.
Entonces, si tengo una clase que es TypeNameMapper y SomethingelseMapper, puedo hacer:
fuente
ICanFly
,ICanRun
,ICanSwim
. Necesitas crear 8 clases de criaturas, una para cada combinación de rasgos (YYY, YYN, YNY, ..., NNY, NNN). SRP indica que implementa cada comportamiento (volar, correr, nadar) una vez y luego implementarlos de manera reutilizable en las 8 criaturas. La composición sería aún mejor aquí, pero ignorando la composición por un segundo, ya debería ver la necesidad de heredar / implementar múltiples comportamientos reutilizables separados debido a SRP.interface
... implementaciones redundantes que deberían estar en la base, ¡que están evolucionando independientemente !, cambios lógicos condicionales que impulsan esta basura por falta de métodos de plantilla, encapsulación rota, abstracción inexistente. Mi favorito: clases de 1 métodointerface
, cada una implementando su propio método 1 , cada una con una sola instancia. ... etc, etc, y etc.Las interfaces y las clases abstractas tienen diferentes propósitos:
En su ejemplo,
interface ITypeNameMapper
define las necesidades de los clientes yabstract class TypeNameMapper
no agrega ningún valor.fuente
public
pertenece al cliente.TypeNameMapper
está agregando mucho valor porque evita que el implementador escriba alguna lógica común.interface
o no solo es relevante, ya que C # prohíbe la herencia múltiple completa.Todo el concepto de interfaces fue creado para soportar una familia de clases que tienen una API compartida.
Esto dice explícitamente que cualquier uso de una interfaz implica que hay (o se espera que haya) más de una implementación de su especificación.
Los marcos DI han enturbiado las aguas aquí, ya que muchos de ellos requieren una interfaz a pesar de que solo habrá una implementación; para mí, esta es una sobrecarga irrazonable para lo que en la mayoría de los casos es solo un medio más complejo y más lento de llamar nuevo, pero Es lo que es.
La respuesta está en la pregunta misma. Si tiene una clase abstracta, se está preparando para la creación de más de una clase derivada con una API común. Por lo tanto, el uso de una interfaz está claramente indicado.
fuente
find ... -exec perl -pi -e ...
y posiblemente corrige errores de compilación triviales. Mi punto es: la ventaja de no tener una cosa (actualmente) inútil es mucho mayor que los posibles ahorros futuros.interface
como código (está hablando de C # -interface
s, no de una interfaz abstracta, como cualquier conjunto de clases / funciones / etc.), y la terminó "... pero aún así proscribe múltiples herencia." No hay necesidad deinterface
s si tiene MI.Si queremos responder explícitamente a la pregunta, el autor dice: "¿Tiene sentido definir esta interfaz si ya tengo una clase abstracta TypeNameMapper o la clase abstracta es suficiente?"
La respuesta es sí y no: sí, debe crear la interfaz aunque ya tenga la clase base abstracta (porque no debería referirse a la clase base abstracta en ningún código de cliente), y no, porque no debería haber creado la clase base abstracta en ausencia de una interfaz.
Es evidente que no ha pensado lo suficiente en la API que está tratando de construir. Presente primero la API, luego proporcione una implementación parcial si lo desea. El hecho de que su clase base abstracta escriba código de verificación es suficiente para decirle que no es la abstracción correcta.
Como en la mayoría de las cosas en OOD, cuando está en el ritmo y tiene su modelo de objetos bien hecho, su código le informará sobre lo que viene después. Comience con una interfaz, implemente esa interfaz en las clases que la necesite. Si se encuentra escribiendo un código similar, extráigalo en una clase base abstracta: es la interfaz lo que es importante, la clase base abstracta es solo un auxiliar y puede haber más de una.
fuente
Esto es bastante difícil de responder sin conocer el resto de la aplicación.
Suponiendo que esté utilizando algún tipo de modelo DI y haya diseñado su código para que sea lo más extensible posible (para que pueda agregar nuevos
TypeNameMapper
fácilmente), diría que sí.Razones para:
interface
lo que no necesitas preocuparte en ninguna implementación secundariainterface
s. Si bien muchos de ellos trabajan con clases abstractas, no siempre es un hecho y creo firmemente en seguir el mismo camino que el otro 99% de los usuarios de una biblioteca siempre que sea posible.Razones contra:
class
/interface
cambia hay un poco de sobrecarga adicionalA fin de cuentas, diría que deberías crear el
interface
. Las razones en contra son bastante insignificantes, pero, aunque es posible usar resúmenes en burla e IoC, a menudo es mucho más fácil con las interfaces. Sin embargo, en última instancia, no criticaría el código de un colega si fuera por el otro lado.fuente