Para mim, isso faz sentido, pois atribui um nome concreto à classe, em vez de confiar no NamedEntity genérico. Por outro lado, existem várias dessas classes que simplesmente não têm propriedades adicionais.
Há alguma desvantagem nessa abordagem?
A abordagem não é ruim, mas existem soluções melhores disponíveis. Em resumo, uma interface seria uma solução muito melhor para isso. A principal razão pela qual interfaces e herança são diferentes aqui é porque você só pode herdar de uma classe, mas pode implementar muitas interfaces .
Por exemplo, considere que você nomeou entidades e entidades auditadas. Você tem várias entidades:
One
não é uma entidade auditada nem uma entidade nomeada. Isso é simples:
public class One
{ }
Two
é uma entidade nomeada, mas não uma entidade auditada. Isso é essencialmente o que você tem agora:
public class NamedEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Two : NamedEntity
{ }
Three
é uma entrada nomeada e auditada. É aqui que você encontra um problema. Você pode criar uma AuditedEntity
classe base, mas não pode Three
herdar ambos AuditedEntity
e NamedEntity
:
public class AuditedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : NamedEntity, AuditedEntity // <-- Compiler error!
{ }
No entanto, você pode pensar em uma solução alternativa AuditedEntity
herdando NamedEntity
. Este é um truque inteligente para garantir que toda classe precise herdar (diretamente) de outra classe.
public class AuditedEntity : NamedEntity
{
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
public class Three : AuditedEntity
{ }
Isso ainda funciona. Mas o que você fez aqui é declarado que toda entidade auditada é inerentemente também uma entidade nomeada . O que me leva ao meu último exemplo. Four
é uma entidade auditada, mas não uma entidade nomeada. Mas você não pode deixar Four
herdar, AuditedEntity
pois você também o tornaria NamedEntity
devido à herança entre AuditedEntity and
NamedEntity`.
Usando a herança, não há como criar os dois Three
e Four
funcionar, a menos que você comece a duplicar as classes (o que abre um novo conjunto de problemas).
Usando interfaces, isso pode ser facilmente alcançado:
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; }
}
A única pequena desvantagem aqui é que você ainda precisa implementar a interface. Mas você obtém todos os benefícios de ter um tipo reutilizável comum, sem nenhuma das desvantagens que surgem quando você precisa de variações em vários tipos comuns para uma determinada entidade.
Mas seu 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 outro lado, existem várias dessas classes que simplesmente não têm propriedades adicionais.
Essa é uma variação no padrão da interface do marcador , em que você implementa uma interface vazia apenas para poder usar o tipo de interface para verificar se uma determinada classe está "marcada" com essa interface.
Você está usando classes herdadas em vez de interfaces implementadas, mas o objetivo é o mesmo, então vou me referir a ela como uma "classe marcada".
No valor nominal, não há nada de errado com interfaces / classes de marcadores. Eles são sintaticamente e tecnicamente válidos e não há desvantagens inerentes ao seu uso, desde que o marcador seja universalmente verdadeiro (em tempo de compilação) e não condicional .
É exatamente assim que você deve diferenciar entre exceções diferentes, mesmo quando essas exceções não têm realmente propriedades / métodos adicionais comparados ao método base.
Portanto, não há nada de errado em fazê-lo, mas eu aconselho a usá-lo com cautela, certificando-se de que você não esteja apenas tentando encobrir um erro arquitetônico existente com polimorfismo mal projetado.
OrderDateInfo
s daqueles que são relevantes para outrasNamedEntity
s