Para mí, esto tiene sentido ya que le da un nombre concreto a la clase, en lugar de confiar en la NamedEntity genérica. Por otro lado, hay una serie de tales clases que simplemente no tienen propiedades adicionales.
¿Hay alguna desventaja en este enfoque?
El enfoque no es malo, pero hay mejores soluciones disponibles. En resumen, una interfaz sería una solución mucho mejor para esto. La razón principal por la cual las interfaces y la herencia son diferentes aquí es porque solo puede heredar de una clase, pero puede implementar muchas interfaces .
Por ejemplo, considere que ha nombrado entidades y entidades auditadas. Tienes varias entidades:
One
no es una entidad auditada ni una entidad nombrada. Así de simple:
public class One
{ }
Two
es una entidad nombrada pero no una entidad auditada. Eso es esencialmente lo que tienes ahora:
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Two : NamedEntity
{ }
Three
es una entrada con nombre y auditada. Aquí es donde te encuentras con un problema. Puede crear una AuditedEntity
clase base, pero no puede hacer que Three
herede ambos AuditedEntity
y NamedEntity
:
public class AuditedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : NamedEntity, AuditedEntity // <-- Compiler error!
{ }
Sin embargo, puede pensar en una solución al AuditedEntity
heredar de NamedEntity
. Este es un truco inteligente para garantizar que cada clase solo necesite heredar (directamente) de otra clase.
public class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : AuditedEntity
{ }
Esto aun funciona. Pero lo que ha hecho aquí se afirma que cada entidad auditada es inherentemente también una entidad con nombre . Lo que me lleva a mi último ejemplo. Four
es una entidad auditada pero no una entidad nombrada. Pero no puede dejar de Four
heredar de AuditedEntity
como lo haría entonces NamedEntity
debido a la herencia entre AuditedEntity and
NamedEntity`.
Usando la herencia, no hay forma de hacer ambas cosas Three
y Four
trabajar a menos que comience a duplicar clases (lo que abre un nuevo conjunto de problemas).
Usando interfaces, esto se puede lograr fácilmente:
public interface INamedEntity
{
int Id { get; set; }
string Name { get; set; }
}
public interface IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class One
{ }
public class Two : INamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Three : INamedEntity, IAuditedEntity
{
public int Id { get; set; }
public string Name { get; set; }
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
public class Four : IAuditedEntity
{
DateTime CreatedOn { get; set; }
DateTime UpdatedOn { get; set; }
}
El único inconveniente menor aquí es que aún tiene que implementar la interfaz. Pero obtiene todos los beneficios de tener un tipo reutilizable común, sin ninguno de los inconvenientes que surgen cuando necesita variaciones en múltiples tipos comunes para una entidad determinada.
Pero tu polimorfismo permanece intacto:
var one = new One();
var two = new Two();
var three = new Three();
var four = new Four();
public void HandleNamedEntity(INamedEntity namedEntity) {}
public void HandleAuditedEntity(IAuditedEntity auditedEntity) {}
HandleNamedEntity(one); //Error - not a named entity
HandleNamedEntity(two);
HandleNamedEntity(three);
HandleNamedEntity(four); //Error - not a named entity
HandleAuditedEntity(one); //Error - not an audited entity
HandleAuditedEntity(two); //Error - not an audited entity
HandleAuditedEntity(three);
HandleAuditedEntity(four);
Por otro lado, hay una serie de tales clases que simplemente no tienen propiedades adicionales.
Esta es una variación en el patrón de la interfaz del marcador , donde implementa una interfaz vacía únicamente para poder usar el tipo de interfaz para verificar si una clase determinada está "marcada" con esta interfaz.
Está utilizando clases heredadas en lugar de interfaces implementadas, pero el objetivo es el mismo, por lo que me referiré a ella como una "clase marcada".
A primera vista, no hay nada malo con las interfaces / clases de marcadores. Son sintácticamente y técnicamente válidos, y no existen inconvenientes inherentes a su uso siempre que el marcador sea universalmente verdadero (en tiempo de compilación) y no condicional .
Así es exactamente cómo debe diferenciar entre diferentes excepciones, incluso cuando esas excepciones no tienen propiedades / métodos adicionales en comparación con el método base.
Por lo tanto, no hay nada inherentemente malo en hacerlo, pero recomendaría usar esto con precaución, asegurándome de que no solo estés tratando de ocultar un error arquitectónico existente con polimorfismo mal diseñado.
OrderDateInfo
s de aquellos que son relevantes para otrosNamedEntity
s