Acredito que a verdadeira razão pela qual muitas pessoas acham que as aulas devam ser final
/ sealed
é que a maioria das classes extensíveis não abstratas não são documentadas adequadamente .
Deixe-me elaborar. A partir de longe, existe a visão entre alguns programadores de que a herança como ferramenta no OOP é amplamente usada e abusada. Todos nós lemos o princípio da substituição de Liskov, embora isso não nos impedisse de violá-lo centenas (talvez até milhares) de vezes.
O problema é que os programadores adoram reutilizar código. Mesmo quando não é uma boa ideia. E a herança é uma ferramenta essencial nesse "reabusing" do código. Voltar à pergunta final / lacrada.
A documentação adequada para uma classe final / selada é relativamente pequena: você descreve o que cada método faz, quais são os argumentos, o valor de retorno, etc. Todas as coisas usuais.
No entanto, ao documentar adequadamente uma classe extensível, você deve incluir pelo menos o seguinte :
- Dependências entre métodos (qual método chama quais, etc.)
- Dependências em variáveis locais
- Contratos internos que a classe estendida deve honrar
- Convenção de chamada para cada método (por exemplo, quando você a substitui, chama o
super
implementação? Você chama no início do método ou no final? Pense em construtor ou destruidor)
- ...
Estes estão no topo da minha cabeça. E posso fornecer um exemplo de por que cada uma delas é importante e ignorá-la irá estragar uma classe estendida.
Agora, considere quanto esforço de documentação deve ser necessário para documentar adequadamente cada uma dessas coisas. Acredito que uma classe com métodos 7-8 (que pode ser muito em um mundo idealizado, mas é muito pouco no real) também pode ter uma documentação de 5 páginas, somente texto. Em vez disso, evitamos a metade do caminho e não selamos a classe, para que outras pessoas possam usá-la, mas também não a documentam adequadamente, pois isso levará uma quantidade enorme de tempo (e, você sabe, pode nunca seja estendido, então por que se preocupar?).
Se você estiver criando uma classe, poderá sentir a tentação de selá-la para que as pessoas não possam usá-la de uma maneira que você não previu (e preparou). Por outro lado, quando você está usando o código de outra pessoa, às vezes a partir da API pública, não há motivo visível para a aula ser final, e você pode pensar "Droga, isso me custou 30 minutos em busca de uma solução alternativa".
Eu acho que alguns dos elementos de uma solução são:
- Primeiro, verifique se a extensão é uma boa idéia quando você é cliente do código e realmente favorece a composição sobre a herança.
- Segundo, leia o manual na íntegra (novamente como cliente) para garantir que você não esteja negligenciando algo mencionado.
- Terceiro, quando você estiver escrevendo um pedaço de código que o cliente usará, escreva a documentação adequada para o código (sim, o longo caminho). Como exemplo positivo, posso fornecer os documentos para iOS da Apple. Eles não são suficientes para que o usuário sempre estenda adequadamente suas classes, mas pelo menos incluem algumas informações sobre herança. O que é mais do que posso dizer para a maioria das APIs.
- Quarto, tente estender sua própria classe, para garantir que ela funcione. Sou um grande defensor da inclusão de muitas amostras e testes nas APIs e, quando você está fazendo um teste, pode testar também as cadeias de herança: afinal, elas fazem parte do seu contrato!
- Quinto, em situações em que você está em dúvida, indique que a classe não deve ser estendida e que fazê-lo é uma má ideia (tm). Indique que você não deve ser responsabilizado por esse uso não intencional, mas ainda não selará a classe. Obviamente, isso não cobre casos em que a classe deve ser 100% selada.
- Por fim, ao selar uma classe, forneça uma interface como um gancho intermediário, para que o cliente possa "reescrever" sua própria versão modificada da classe e contornar a classe 'selada'. Dessa forma, ele pode substituir a classe selada por sua implementação. Agora, isso deve ser óbvio, já que é um acoplamento solto em sua forma mais simples, mas ainda vale a pena mencionar.
Também vale a pena mencionar a seguinte pergunta "filosófica": É se uma classe é sealed
/ final
parte do contrato da classe ou um detalhe de implementação? Agora eu não quero ir para lá, mas uma resposta para isso também deve influenciar sua decisão de selar ou não uma classe.
Open/Closed principle
, não oClosed Principle.