Como desenvolvedor de C ++, estou acostumado a arquivos de cabeçalho em C ++ e considero benéfico ter algum tipo de "documentação" forçada dentro do código. Normalmente, me divirto muito quando preciso ler um código C # por causa disso: não tenho esse tipo de mapa mental da classe com a qual estou trabalhando.
Vamos supor que, como engenheiro de software, estou projetando a estrutura de um programa. Seria muito louco definir cada classe como uma classe abstrata não implementada, de forma semelhante ao que faríamos com cabeçalhos de C ++, e permitir que os desenvolvedores a implementassem?
Acho que pode haver algumas razões pelas quais alguém pode achar que essa é uma solução terrível, mas não sei por que. O que alguém teria que considerar para uma solução como essa?
fonte
Respostas:
A razão pela qual isso foi feito em C ++ tinha a ver com tornar os compiladores mais rápidos e fáceis de implementar. Este não foi um projeto para facilitar a programação .
O objetivo dos arquivos de cabeçalho era permitir que o compilador fizesse uma primeira passagem super rápida para conhecer todos os nomes de funções esperados e alocar os locais de memória para eles, para que pudessem ser referenciados quando chamados em arquivos cp, mesmo que a classe que os definisse tivesse ainda não foi analisado.
Não é recomendável tentar replicar uma consequência de limitações de hardware antigas em um ambiente de desenvolvimento moderno!
Definir uma interface ou classe abstrata para cada classe reduzirá sua produtividade; o que mais você poderia ter feito com esse tempo ? Além disso, outros desenvolvedores não seguirão esta convenção.
De fato, outros desenvolvedores podem excluir suas classes abstratas. Se eu encontrar uma interface no código que atenda a esses dois critérios, eu a apago e refatoro do código:
1. Does not conform to the interface segregation principle
2. Only has one class that inherits from it
A outra coisa é que existem ferramentas incluídas no Visual Studio que fazem o que você deseja realizar automaticamente:
Class View
Object Browser
Solution Explorer
você pode clicar em triângulos para gastar classes para ver suas funções, parâmetros e tipos de retorno.Experimente uma das opções acima antes de dedicar tempo à replicação dos arquivos de cabeçalho C ++ em C #.
Além disso, existem razões técnicas para não fazer isso ... ele tornará seu binário final maior do que o necessário. Vou repetir este comentário de Mark Benningfield:
Além disso, mencionado por Robert Harvey, tecnicamente, o equivalente mais próximo de um cabeçalho em C # seria uma interface, não uma classe abstrata.
fonte
Primeiro, entenda que uma classe puramente abstrata é realmente apenas uma interface que não pode fazer herança múltipla.
Classe de gravação, interface de extração, é uma atividade com morte cerebral. Tanto é assim que temos uma refatoração para isso. O que é uma pena. Após esse padrão "toda classe obtém uma interface", não apenas produz desordem, que perde completamente o objetivo.
Uma interface não deve ser pensada como uma simples reformulação formal do que a classe pode fazer. Uma interface deve ser pensada como um contrato imposto pelo código do cliente usando detalhando suas necessidades.
Não tenho problemas em escrever uma interface que atualmente tenha apenas uma classe implementando-a. Na verdade, eu não me importo se nenhuma aula ainda a implementa. Porque estou pensando no que meu código de uso precisa. A interface expressa o que o código em uso exige. O que vier depois pode fazer o que quiser, desde que satisfaça essas expectativas.
Agora não faço isso toda vez que um objeto usa outro. Eu faço isso ao atravessar uma fronteira. Faço isso quando não quero que um objeto saiba exatamente com qual outro objeto está falando. Qual é a única maneira de o polimorfismo funcionar. Faço isso quando espero que o objeto que meu código de cliente está falando provavelmente mude. Certamente não faço isso quando estou usando a classe String. A classe String é agradável e estável e não sinto necessidade de me proteger contra isso.
Quando você decide interagir diretamente com uma implementação concreta, e não através de uma abstração, está prevendo que a implementação é estável o suficiente para confiar em não mudar.
É assim que tempero o Princípio da Inversão da Dependência . Você não deve aplicar cegamente fanaticamente isso a tudo. Quando você adiciona uma abstração, está realmente dizendo que não confia que a escolha da classe de implementação seja estável ao longo da vida do projeto.
Tudo isso pressupõe que você esteja tentando seguir o Princípio Aberto Fechado . Esse princípio é importante apenas quando os custos associados à realização de alterações diretas no código estabelecido são significativos. Uma das principais razões pelas quais as pessoas discordam sobre a importância da dissociação de objetos é porque nem todos experimentam os mesmos custos ao fazer mudanças diretas. Se retestar, recompilar e redistribuir toda a sua base de código for trivial para você, resolver a necessidade de alteração com modificação direta provavelmente será uma simplificação muito atraente desse problema.
Simplesmente não existe uma resposta de morte cerebral para essa pergunta. Uma interface ou classe abstrata não é algo que você deve adicionar a todas as classes e você não pode apenas contar o número de classes de implementação e decidir que não é necessário. É sobre lidar com mudanças. O que significa que você está antecipando o futuro. Não se surpreenda se você errar. Mantenha o mais simples possível, sem recuar no canto.
Portanto, não escreva abstrações apenas para nos ajudar a ler o código. Temos ferramentas para isso. Use abstrações para dissociar o que precisa ser dissociado.
fonte
Sim, isso seria terrível porque (1) introduz código desnecessário (2) confunde o leitor.
Se você deseja programar em C #, deve se acostumar a ler C #. O código escrito por outras pessoas não seguirá esse padrão de qualquer maneira.
fonte
Testes unitários
Eu sugeriria fortemente que você escrevesse testes de unidade em vez de sobrecarregar o código com interfaces ou classes abstratas (exceto quando necessário por outros motivos).
Um teste de unidade bem escrito não apenas descreve a interface da sua classe (como um arquivo de cabeçalho, classe abstrata ou interface faria), mas também descreve, por exemplo, a funcionalidade desejada.
Exemplo: Onde você pode ter escrito um arquivo de cabeçalho myclass.h assim:
Em vez disso, em c #, escreva um teste como este:
Este teste muito simples transmite as mesmas informações que o arquivo de cabeçalho. (Deveríamos ter uma classe chamada "MyClass" com uma função sem parâmetros "Foo"). Enquanto um arquivo de cabeçalho é mais compacto, o teste contém muito mais informações.
Uma ressalva: esse processo de ter um engenheiro de software sênior fornecendo (falhando) testes para outros desenvolvedores para resolver conflitos violentamente com metodologias como TDD, mas no seu caso, seria uma grande melhoria.
fonte