Quando usar classes abstratas em vez de interfaces com métodos de extensão em C #?

70

"Classe abstrata" e "interface" são conceitos semelhantes, sendo a interface o mais abstrato dos dois. Um fator de diferenciação é que as classes abstratas fornecem implementações de métodos para classes derivadas quando necessário. No C #, no entanto, esse fator de diferenciação foi reduzido pela recente introdução de métodos de extensão, que permitem implementações para métodos de interface. Outro fator de diferenciação é que uma classe pode herdar apenas uma classe abstrata (ou seja, não há herança múltipla), mas pode implementar várias interfaces. Isso torna as interfaces menos restritivas e mais flexíveis. Então, em C #, quando devemos usar classes abstratas em vez de interfaces com métodos de extensão?

Um exemplo notável do modelo de método de interface + extensão é o LINQ, onde a funcionalidade de consulta é fornecida para qualquer tipo implementado IEnumerablepor meio de vários métodos de extensão.

Gulshan
fonte
2
E também podemos usar 'Propriedades' nas interfaces.
precisa saber é o seguinte
11
Soa como uma questão específica do C #, em vez de uma pergunta geral do OOD. (A maioria das outras linguagens OO não têm métodos de extensão, nem propriedades.)
Péter Török
4
Os métodos de extensão não resolvem completamente o problema que as classes abstratas resolvem; portanto, o motivo não é diferente do que é em Java ou em alguma outra linguagem de herança única semelhante.
Job
3
A necessidade de implementações de métodos de classe abstrata não foi reduzida por métodos de extensão. Os métodos de extensão não podem acessar campos de membros particulares, nem podem ser declarados virtuais e substituídos em classes derivadas.
precisa saber é o seguinte

Respostas:

63

Quando você precisar que uma parte da classe seja implementada. O melhor exemplo que usei é o padrão do método de modelo.

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

Assim, você pode definir as etapas que serão executadas quando Do () for chamado, sem saber as especificidades de como elas serão implementadas. As classes derivadas devem implementar os métodos abstratos, mas não o método Do ().

Os métodos de extensão não necessariamente atendem à parte "deve fazer parte da classe" da equação. Além disso, iirc, os métodos de extensão não podem (aparentemente) ter nada além de escopo público.

editar

A pergunta é mais interessante do que eu havia originalmente creditado. Após um exame mais aprofundado, Jon Skeet respondeu a uma pergunta como essa no SO em favor do uso de interfaces + métodos de extensão. Além disso, uma possível desvantagem é usar a reflexão contra uma hierarquia de objetos projetada dessa maneira.

Pessoalmente, estou tendo problemas para perceber o benefício de alterar uma prática comum atualmente, mas também vejo poucas ou nenhuma desvantagem em fazer isso.

Deve-se notar que é possível programar dessa maneira em vários idiomas através das classes Utility. As extensões apenas fornecem o açúcar sintático para fazer com que os métodos pareçam pertencer à classe.

Steven Evers
fonte
Bata-me a ele ..
Giorgi
11
Mas o mesmo pode ser feito com interfaces e métodos de extensão. Lá, os métodos que você está definindo na classe base abstrata Do()serão definidos como um método de extensão. E os métodos deixados para a definição de classes derivadas DoThis()serão membros da interface principal. E com interfaces, você pode suportar várias implementações / herança. E veja this-odetocode.com/blogs/scott/archive/2009/10/05/…
Gulshan
@Gulshan: Porque uma coisa pode ser feita de mais de uma maneira, não invalida a maneira escolhida. Não há vantagem em usar interfaces. A própria classe abstrata é a interface. Você não precisa de interfaces para suportar várias implementações.
ThomasX
Além disso, pode haver desvantagens no modelo de interface + extensão. Veja a biblioteca Linq, por exemplo. É ótimo, mas se você precisar usá-lo apenas uma vez nesse arquivo de código, a biblioteca Linq completa estará disponível no IntelliSense em TODAS AS IEnumerable no seu arquivo de código. Isso pode diminuir consideravelmente o desempenho do IDE.
21412 KeithS
-1 porque você não demonstraram qualquer maneira em que as classes abstratas são um ajuste melhor para Template Method do que as interfaces
Benjamin Hodgson
25

É tudo sobre o que você deseja modelar:

Classes abstratas trabalham por herança . Sendo apenas classes base especiais, elas modelam alguma relação é uma relação.

Por exemplo, um cachorro é um animal, então temos

class Dog : Animal { ... }

Como não faz sentido criar realmente um animal genérico (como seria?), Criamos essa Animalclasse base abstract- mas ainda é uma classe base. E a Dogclasse não faz sentido sem ser uma Animal.

As interfaces, por outro lado, são uma história diferente. Eles não usam herança, mas fornecem polimorfismo (que também pode ser implementado com herança). Eles não modelam um relacionamento é-um , mas mais do que isso suporta .

Veja, por exemplo IComparable- um objeto que suporta ser comparado com outro .

A funcionalidade de uma classe não depende das interfaces implementadas, a interface apenas fornece uma maneira genérica de acessar a funcionalidade. Ainda poderíamos fazer Disposeum Graphicsou um FileStreamsem tê-los implementados IDisposable. A interface apenas relaciona os métodos.

Em princípio, pode-se adicionar e remover interfaces como extensões, sem alterar o comportamento de uma classe, apenas enriquecendo o acesso a ela. Você não pode tirar uma classe base, pois o objeto se tornaria sem sentido!

Dario
fonte
Quando você escolheria manter um resumo da classe base?
precisa saber é o seguinte
11
@ Gulshan: Se, por algum motivo, não fizer sentido, na verdade, criar uma instância da classe base. Você pode "criar" um chihuahua ou um tubarão branco, mas não pode criar um animal sem especialização adicional - assim você faria Animalabstrato. Isso permite que você escreva abstractfunções para serem especializadas pelas classes derivadas. Ou mais em termos de programação - provavelmente não faz sentido criar a UserControl, mas como a Button é a UserControl, temos o mesmo tipo de relacionamento de classe / classe básica.
Dario
se você tem métodos abstratos, então você tem classe abstrata. E métodos abstratos que você tem quando não há valor para implementá-los (como "ir" para animal). E, é claro, às vezes você não deseja permitir objetos de alguma classe geral, além de torná-lo abstrato.
Dainius 15/06
Não existe uma regra que diga que, se for gramatical e semanticamente significativo dizer "B é um A", então A deve ser uma classe abstrata. Você deve preferir interfaces até realmente não poder evitar a classe. O compartilhamento de código pode ser feito através da composição de interfaces, etc. Isso facilita evitar se pintar em um canto com árvores de herança estranhas.
Sara
3

Acabei de perceber que não uso classes abstratas (pelo menos não criadas) há anos.

A classe abstrata é uma fera um tanto estranha entre interface e implementação. Há algo nas aulas abstratas que formiga meu senso de aranha. Eu diria que não se deve "expor" a implementação por trás da interface de nenhuma maneira (ou fazer quaisquer vínculos).

Mesmo se houver uma necessidade de comportamento comum entre as classes que implementam uma interface, eu usaria uma classe auxiliar independente, em vez de uma classe abstrata.

Eu iria para interfaces.

Maglob
fonte
2
-1: Não tenho certeza de quantos anos você tem, mas convém aprender padrões de design, particularmente o padrão do método de modelo. Os padrões de design farão de você um programador melhor e acabarão com sua ignorância das classes abstratas.
Jim G.
O mesmo sobre a sensação de formigamento. As classes, o primeiro e mais importante recurso de C # e Java, são um recurso para criar um tipo e encadear imediatamente para sempre uma implementação.
Jesse Millikan
2

IMHO, acho que há uma mistura de conceitos aqui.

As classes usam um certo tipo de herança, enquanto as interfaces usam um tipo diferente de herança.

Ambos os tipos de herança são importantes e úteis, e cada um tem seus prós e contras.

Vamos começar no início.

A história com interfaces começa há muito tempo com C ++. Em meados da década de 1980, quando o C ++ foi desenvolvido, a idéia de interfaces como um tipo diferente de tipo ainda não era concretizada (chegaria a tempo).

O C ++ tinha apenas um tipo de herança, o tipo de herança usado pelas classes, chamado herança de implementação.

Para poder aplicar a recomendação do GoF Book: "Para programar para a interface, e não para a implementação", o C ++ suportou classes abstratas e herança múltipla de implementação para poder fazer quais interfaces em C # são capazes (e úteis!) De executar .

O C # apresenta um novo tipo de tipo, interfaces e um novo tipo de herança, a herança da interface, para oferecer pelo menos duas maneiras diferentes de oferecer suporte a "Programar para a interface e não para a implementação", com uma ressalva importante: somente C # suporta herança múltipla com herança de interface (e não com herança de implementação).

Portanto, os motivos arquitetônicos por trás das interfaces em C # são a recomendação do GoF.

Espero que isso possa ajudar.

Atenciosamente, Gastón

Gastón E. Nusimovich
fonte
1

A aplicação de métodos de extensão a uma interface é útil para aplicar um comportamento comum entre classes que podem compartilhar apenas uma interface comum. Tais classes podem já existir e ser fechadas para extensões por outros meios.

Para novos projetos, eu preferiria o uso de métodos de extensão em interfaces. Para uma hierarquia de Tipos, os métodos de extensão fornecem um meio de estender o comportamento sem precisar abrir toda a hierarquia para modificação.

No entanto, os métodos de extensão não conseguem acessar a implementação privada, portanto, no topo de uma hierarquia, se eu precisar encapsular a implementação privada por trás de uma interface pública, uma classe abstrata seria a única maneira.

Ed James
fonte
Não, não é o único caminho seguro. Existe outra maneira mais segura. Esse é o método de extensão. Os clientes não serão infectados. É por isso que mencionei interfaces + métodos de extensão.
precisa saber é o seguinte
@ Ed James Eu acho "menos poderoso" = "mais flexível" Quando você escolheria uma classe abstrata mais poderosa em vez de uma interface mais flexível?
precisa saber é o seguinte
@ James Ed No métodos de extensão asp.mvc foram utilizados desde o início. O que você acha disso?
22611 Gulshan
0

Eu acho que em C # herança múltipla não é suportada e é por isso que o conceito de interface é introduzido em C #.

No caso de classes abstratas, a herança múltipla também não é suportada. Portanto, é fácil decidir se as classes ou interfaces abstratas devem ser usadas.

Principiante
fonte
Para ser preciso, são classes abstratas puras que são chamadas de "interfaces" em C #.
Johan Boulé 16/05
-1

Estou no processo de implementar uma classe Abstract no meu projeto simplesmente porque o C # não suporta operadores (conversões implícitas, no meu caso) em interfaces.

palswim
fonte