Eu costumava criar muitas classes / métodos abstratos. Então eu comecei a usar interfaces.
Agora não tenho certeza se as interfaces não estão tornando obsoletas as classes abstratas.
Você precisa de uma aula totalmente abstrata? Crie uma interface em seu lugar. Você precisa de uma classe abstrata com alguma implementação? Crie uma interface, crie uma classe. Herdar a classe, implemente a interface. Um benefício adicional é que algumas classes podem não precisar da classe pai, mas apenas implementam a interface.
Então, as classes / métodos abstratos são obsoletos?
object-oriented
architecture
inheritance
interfaces
Boris Yankov
fonte
fonte
Respostas:
Não.
As interfaces não podem fornecer implementação padrão, classes abstratas e métodos. Isso é especialmente útil para evitar a duplicação de código em muitos casos.
Essa também é uma maneira muito boa de reduzir o acoplamento seqüencial. Sem método / classes abstratos, você não pode implementar o padrão de método de modelo. Sugiro que você consulte este artigo da wikipedia: http://en.wikipedia.org/wiki/Template_method_pattern
fonte
Existe um método abstrato para que você possa chamá-lo de dentro da sua classe base, mas implementá-lo em uma classe derivada. Portanto, sua classe base sabe:
Essa é uma maneira razoavelmente boa de implementar um furo no padrão intermediário sem que a classe derivada conheça as atividades de configuração e limpeza. Sem métodos abstratos, você teria que confiar na classe derivada implementando
DoTask
e lembrando-se de chamarbase.DoSetup()
ebase.DoCleanup()
o tempo todo.Editar
Além disso, obrigado ao deadalnix por postar um link para o Template Method Pattern , que é o que eu descrevi acima sem realmente saber o nome. :)
fonte
public void DoTask(Action doWork)
Fruit
e sua classe derivada éApple
, você deseja chamarmyApple.Eat()
, nãomyApple.Eat((a) => howToEatApple(a))
. Além disso, você não querApple
ter que ligarbase.Eat(() => this.howToEatMe())
. Eu acho que é mais limpo substituir um método abstrato.Não, eles não são obsoletos.
De fato, existe uma diferença obscura, porém fundamental, entre Classes / Métodos Abstratos e Interfaces.
se o conjunto de classes em que uma delas deve ser usada tiver um comportamento comum que eles compartilham (classes relacionadas, quero dizer), então vá para Classes / métodos abstratos.
Exemplo: funcionário, oficial, diretor - todas essas classes têm CalculateSalary () em comum, usam classes base abstratas.
Se suas classes não tiverem nada em comum (classes não relacionadas, no contexto escolhido) entre elas, mas tiverem uma ação muito diferente na implementação, vá para Interface.
Exemplo: vaca, banco, carro, classes não relacionadas ao telescópio, mas o Isortable pode estar lá para classificá-las em uma matriz.
Essa diferença é geralmente ignorada quando abordada de uma perspectiva polimórfica. Mas, pessoalmente, sinto que há situações em que uma é mais adequada que a outra pela razão explicada acima.
fonte
Além das outras boas respostas, há uma diferença fundamental entre interfaces e classes abstratas que ninguém mencionou especificamente, a saber, que as interfaces são muito menos confiáveis e, portanto, impõem uma carga de teste muito maior do que as classes abstratas. Por exemplo, considere este código C #:
Estou garantido que existem apenas dois caminhos de código que preciso testar. O autor de Frobbit pode confiar no fato de que o frobber é vermelho ou verde.
Se, em vez disso, dizemos:
Agora eu não sei absolutamente nada sobre os efeitos dessa ligação para Frob lá. Eu preciso ter certeza de que todo o código do Frobbit é robusto contra qualquer possível implementação do IFrobber , mesmo implementações de pessoas que são incompetentes (ruins) ou ativamente hostis a mim ou aos meus usuários (muito piores).
As classes abstratas permitem evitar todos esses problemas; usa-os!
fonte
Você mesmo diz:
isso soa muito trabalho em comparação com 'herdar a classe abstrata'. Você pode trabalhar por si mesmo abordando o código de uma visão 'purista', mas acho que já tenho o suficiente para fazer isso sem tentar adicionar à minha carga de trabalho sem nenhum benefício prático.
fonte
Como comentei no post do @deadnix: As implementações parciais são um anti-padrão, apesar do fato de que o padrão do modelo as formaliza.
Uma solução limpa para este exemplo da wikipedia do padrão de modelo :
Esta solução é melhor, porque usa composição em vez de herança . O padrão de modelo introduz uma dependência entre a implementação das regras de monopólio e a implementação de como os jogos devem ser executados. No entanto, essas são duas responsabilidades completamente diferentes e não há boas razões para associá-las.
fonte
Não. Mesmo sua alternativa proposta inclui o uso de classes abstratas. Além disso, como você não especificou o idioma, vou seguir em frente e dizer que o código genérico é a melhor opção do que a herança frágil de qualquer maneira. Classes abstratas têm vantagens significativas sobre interfaces.
fonte
Classes abstratas não são interfaces. São classes que não podem ser instanciadas.
Mas então você teria uma classe inútil não abstrata. Métodos abstratos são necessários para preencher o furo de funcionalidade na classe base.
Por exemplo, dada esta classe
Como você implementaria essa funcionalidade básica em uma classe que não tem nem
Frob()
nemIsFrobbingNeeded
?fonte
Eu sou um criador da estrutura de servlet, onde as classes abstratas desempenham um papel essencial. Eu diria mais: preciso de métodos semi-abstratos, quando um método precisa ser substituído em 50% dos casos e eu gostaria de ver um aviso do compilador sobre esse método não ter sido substituído. Eu resolvo o problema adicionando anotações. Voltando à sua pergunta, existem dois casos de uso diferentes de classes abstratas e interfaces, e ninguém está obsoleto até agora.
fonte
Eu não acho que eles sejam obsoletos por interfaces, mas podem ser obsoletos pelo padrão de estratégia.
O uso primário de uma classe abstrata é adiar uma parte da implementação; para dizer, "esta parte da implementação da classe pode ser diferente".
Infelizmente, uma classe abstrata força o cliente a fazer isso por herança . Considerando que o padrão de estratégia permitirá alcançar o mesmo resultado sem nenhuma herança. O cliente pode criar instâncias da sua classe em vez de sempre definir a sua própria, e a "implementação" (comportamento) da classe pode variar dinamicamente. O padrão de estratégia tem a vantagem adicional de que o comportamento pode ser alterado no tempo de execução, não apenas no tempo de design, e também possui um acoplamento significativamente mais fraco entre os tipos envolvidos.
fonte
Geralmente, tenho enfrentado muito, muito, mais problemas de manutenção associados a interfaces puras do que os ABCs, mesmo os ABCs usados com herança múltipla. YMMV - não sei, talvez nossa equipe apenas os tenha usado inadequadamente.
Dito isto, se usarmos uma analogia do mundo real, quanto uso há para interfaces puras completamente desprovidas de funcionalidade e estado? Se eu usar o USB como exemplo, é uma interface razoavelmente estável (acho que estamos no USB 3.2 agora, mas também manteve a compatibilidade com versões anteriores).
No entanto, não é uma interface sem estado. Não é desprovido de funcionalidade. É mais como uma classe base abstrata do que como uma interface pura. Na verdade, é mais perto de uma classe concreta com requisitos funcionais e de estado muito específicos, sendo a única abstração o que se conecta à porta e a única parte substituível.
Caso contrário, seria apenas um "buraco" no seu computador com um fator de forma padronizado e requisitos funcionais muito mais frouxos que não fariam nada por si só até que cada fabricante tivesse seu próprio hardware para fazer esse buraco fazer algo, nesse ponto torna-se um padrão muito mais fraco e nada mais que um "buraco" e uma especificação do que deve fazer, mas nenhuma provisão central de como fazê-lo. Enquanto isso, podemos terminar com 200 maneiras diferentes de fazer isso, depois que todos os fabricantes de hardware tentarem criar suas próprias maneiras de anexar funcionalidade e estado a esse "buraco".
E nesse ponto, podemos ter certos fabricantes que apresentam problemas diferentes em relação a outros. Se precisarmos atualizar a especificação, poderemos ter 200 implementações de portas USB concretas diferentes, com maneiras totalmente diferentes de lidar com a especificação que precisam ser atualizadas e testadas. Alguns fabricantes podem desenvolver implementações padrão de fato que compartilham entre si (sua classe base analógica implementando essa interface), mas não todas. Algumas versões podem ser mais lentas que outras. Alguns podem ter melhor rendimento, mas pior latência ou vice-versa. Alguns podem usar mais energia da bateria que outros. Alguns podem desbotar e não funcionar com todo o hardware que deveria funcionar com portas USB. Alguns podem exigir que um reator nuclear seja conectado para operar, o que tem tendência a envenenar os usuários.
E foi o que eu encontrei, pessoalmente, com interfaces puras. Pode haver alguns casos em que eles fazem sentido, como apenas para modelar o fator de forma de uma placa-mãe contra um gabinete de CPU. As analogias do fator de forma são, de fato, praticamente sem estado e sem funcionalidade, como no "buraco" analógico. Mas, muitas vezes, considero um grande erro as equipes considerarem que, de alguma forma, é superior em todos os casos, nem mesmo perto.
Pelo contrário, acho que muito mais casos seriam resolvidos melhor pelos ABCs do que pelas interfaces, se essas forem as duas opções, a menos que sua equipe seja tão gigantesca que seja realmente desejável ter o equivalente analógico acima de 200 implementações USB concorrentes em vez de um padrão central para manter. Em uma antiga equipe em que participei, tive que lutar muito para afrouxar o padrão de codificação para permitir ABCs e herança múltipla, e principalmente em resposta a esses problemas de manutenção descritos acima.
fonte