Se assumirmos que não é desejável que a classe base seja uma classe de interface pura, e usando os 2 exemplos abaixo, qual é a melhor abordagem, usando a definição de classe de método abstrato ou virtual?
A vantagem da versão "abstrata" é que ela provavelmente parece mais limpa e força a classe derivada a fornecer uma implementação esperançosamente significativa.
A vantagem da versão "virtual" é que ela pode ser facilmente acessada por outros módulos e usada para teste sem adicionar um monte de estrutura subjacente, como a versão abstrata exige.
Versão abstrata:
public abstract class AbstractVersion
{
public abstract ReturnType Method1();
public abstract ReturnType Method2();
.
.
public abstract ReturnType MethodN();
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
Versão virtual:
public class VirtualVersion
{
public virtual ReturnType Method1()
{
return ReturnType.NotImplemented;
}
public virtual ReturnType Method2()
{
return ReturnType.NotImplemented;
}
.
.
public virtual ReturnType MethodN()
{
return ReturnType.NotImplemented;
}
//////////////////////////////////////////////
// Other class implementation stuff is here
//////////////////////////////////////////////
}
return ReturnType.NotImplemented
? Seriamente? Se você não pode rejeitar o tipo não implementado em tempo de compilação (você pode; usar métodos abstratos) pelo menos lance uma exceção.Respostas:
Meu voto, se eu estivesse consumindo suas coisas, seria pelos métodos abstratos. Isso combina com "falhar cedo". No momento da declaração, pode ser difícil adicionar todos os métodos (embora qualquer ferramenta de refatoração decente faça isso rapidamente), mas pelo menos eu sei qual é o problema imediatamente e o corrijo. Prefiro depurar 6 meses e 12 pessoas em alterações depois para ver por que de repente estamos recebendo uma exceção não implementada.
fonte
A versão virtual é propensa a erros e semanticamente incorreta.
O resumo está dizendo "este método não é implementado aqui. Você deve implementá-lo para fazer essa classe funcionar"
Virtual está dizendo "Eu tenho uma implementação padrão, mas você pode me mudar se precisar"
Se seu objetivo final é a testabilidade, as interfaces normalmente são a melhor opção. (esta classe faz x em vez de esta classe é ax). Talvez você precise dividir suas classes em componentes menores para que isso funcione bem.
fonte
Isso depende do uso da sua turma.
Se os métodos tiverem uma implementação "vazia" razoável, você terá vários métodos e geralmente substituirá apenas alguns deles; o uso de
virtual
métodos faz sentido. Por exemplo,ExpressionVisitor
é implementado dessa maneira.Caso contrário, acho que você deve usar
abstract
métodos.Idealmente, você não deve ter métodos que não são implementados, mas, em alguns casos, essa é a melhor abordagem. Mas se você decidir fazer isso, esses métodos devem ser lançados
NotImplementedException
, não retornando algum valor especial.fonte
Eu sugiro que você reconsidere ter uma interface separada definida que sua classe base implementa e siga a abordagem abstrata.
Código de imagem como este:
Fazer isso resolve estes problemas:
Por ter todo o código que usa objetos derivados de AbstractVersion agora pode ser implementado para receber a interface IVersion. Isso significa que eles podem ser mais facilmente testados em unidade.
A versão 2 do seu produto pode implementar uma interface IVersion2 para fornecer funcionalidade adicional sem quebrar o código de clientes existente.
por exemplo.
Também vale a pena ler sobre inversão de dependência, para impedir que essa classe contenha dependências codificadas que impedem testes de unidade eficazes.
fonte
A injeção de dependência depende de interfaces. Heres um pequeno exemplo. O aluno da turma tem uma função chamada CreateStudent que requer um parâmetro que implemente a interface "IReporting" (com um método ReportAction). Depois de criar um aluno, ele chama ReportAction no parâmetro concreto da classe. Se o sistema estiver configurado para enviar um email após a criação de um aluno, enviaremos uma classe concreta que envia um email em sua implementação ReportAction, ou podemos enviar outra classe concreta que envia saída para uma impressora em sua implementação ReportAction. Ótimo para reutilização de código.
fonte