Então, eu queria herdar de um sealed class
em csharp e me queimei. Simplesmente não há como separá-lo, a menos que você tenha acesso à fonte.
Então isso me fez pensar "por que sealed
existe?". há 4 meses. Não consegui descobrir, apesar de ler muitas coisas, como:
- Os desejos de Jon Skeet " foram selados por padrão no .NET " .
- Prefere composição sobre herança?
- "Você não deve selar todas as classes (...)"
- Como você zomba de uma classe selada?
Eu tentei digerir tudo isso desde então, mas é demais para mim. Eventualmente, ontem eu tentei novamente. Examinei todos eles novamente e mais alguns:
- Por que uma classe deveria ser outra coisa senão "abstrata" ou "final / selada"?
- Em mais de 15 anos de programação, pela primeira vez, ouvi falar do SOLID , de uma resposta de uma pergunta já vinculada e, obviamente, não li tudo há 4 meses
Finalmente, depois de muita reflexão, decidi editar fortemente a pergunta original com base no novo título.
A velha pergunta era muito ampla e subjetiva. Era basicamente perguntando:
- No título antigo: Um bom motivo para usar selado
- No corpo: Como modificar corretamente uma classe selada? Esqueça a herança? Usar composição?
Mas agora, entendendo (o que eu não fiz ontem) que tudo sealed
faz é impedir a herança , e nós podemos e devemos realmente usar a composição sobre a herança , percebi que o que eu precisava era de exemplos práticos .
Acho que minha pergunta aqui é (e de fato sempre foi) exatamente o que o Sr.Mindor me sugeriu em um bate - papo : como o design de herança pode causar um custo extra?
fonte
Respostas:
Isso não é tão difícil de entender.
sealed
foi criado ESPECIFICAMENTE para a Microsoft, a fim de facilitar suas vidas, economizar muito dinheiro e ajudar sua reputação. Como é um recurso de idioma, todos os outros também podem usá-lo, mas você provavelmente nunca precisará usar o selo.A maioria das pessoas reclama que não consegue estender uma aula e, se o fizerem, dizem que todo mundo sabe que é responsabilidade dos desenvolvedores fazê-la funcionar corretamente. Isso está correto, EXCETO essas mesmas pessoas não têm idéia do histórico do Windows e um dos problemas
sealed
está tentando resolver.Vamos supor que um desenvolvedor estendeu uma classe principal .Net (porque não existia lacrado) e a fez funcionar perfeitamente. Yay, para o desenvolvedor. O desenvolvedor entrega o produto ao cliente. O aplicativo do desenvolvedor funciona muito bem. O cliente está feliz. A vida é boa.
Agora, a Microsoft lança um novo sistema operacional, que inclui a correção de bugs nessa classe principal .Net específica. O cliente deseja acompanhar os horários e escolhe instalar o novo sistema operacional. De repente, o aplicativo que o cliente tanto gosta não funciona mais, porque não levou em conta os bugs corrigidos na classe principal .Net.
Quem é o culpado?
Quem conhece a história da Microsoft sabe que o novo sistema operacional da Microsoft será o culpado, e não o software aplicativo que utilizou mal as bibliotecas do Windows. Portanto, torna-se tarefa da Microsoft corrigir o problema, em vez da empresa de aplicativos que criou o problema. Essa é uma das razões pelas quais o código do Windows ficou inchado. Pelo que li, o código do sistema operacional Windows está repleto de if-then específicos, para verificar se um aplicativo e uma versão específicos estão em execução e, em caso afirmativo, faça um processamento especial para permitir que o programa funcione. É muito dinheiro gasto pela Microsoft para corrigir a incompetência de outra empresa.
O uso
sealed
não elimina completamente o cenário acima, mas ajuda.fonte
sealed
por padrão, a menos que sejam especificamente lacradas, simplesmente porque poucas classes são realmente projetadas para serem herdadas. É muito mais provável que sua turma não tenha sido criada para dar suporte a ela, e você deve garantir que gastou esse tempo de desenvolvimento se estiver se esforçando para marcá-la como não lacrada.sealed
. Se de fato fosse a opção padrão, isso significaria que se alguém estivesse herdando de um tipo, saberia que ele foi criado para suportá-lo.String
e depois a mutação após a verificação de segurança, mas antes da E / S? Acredito que esse tipo de cenário é o motivo pelo qual o JavaString
é marcadofinal
(semelhante asealed
) e aposto que a mesma lógica está subjacente a algumas decisões em C #.sealed
é usado para indicar que o escritor da classe não a projetou para ser herdado. Nada menos, nada mais.Projetar adequadamente uma interface já é bastante difícil para muitos programadores. O design adequado de uma classe que pode ser potencialmente herdada adiciona dificuldade e linhas de código. Mais linhas de código escritas significa mais código a ser mantido. Para evitar essa dificuldade crescente,
sealed
pode mostrar que a classe nunca teve a intenção de ser herdada e , nesse contexto, selar uma classe é uma solução perfeitamente válida para um problema .Imagine o caso de onde a classe
CustomBoeing787
é derivadaAirliner
. IssoCustomBoeing787
substitui alguns métodos protegidos deAirliner
, mas não todos. Se o desenvolvedor tiver tempo suficiente e valer a pena, ele poderá testar a classe da unidade para garantir que tudo funcione conforme o esperado, mesmo em um contexto em que métodos não substituídos sejam chamados (por exemplo, setters protegidos que alteram o estado do objeto). Se o mesmo desenvolvedor (1) não tiver tempo, (2) não quiser gastar centenas ou milhares de dólares adicionais de seu chefe / cliente, ou (3) não quiser escrever código adicional, ele não terá precisa agora (YAGNI), então ele pode:libere o código como está, considerando que é a preocupação dos programadores que manterão esse projeto posteriormente para se preocupar com possíveis erros,
ou marque a classe de modo
sealed
a mostrar explicitamente aos futuros programadores que essa classe não se destina a ser herdada e que a remoção do lacre e a utilização como pai devem ser feitas por sua conta e risco.É uma boa idéia selar o maior número possível de classes ou o menor número possível? Da minha experiência, não há resposta definitiva. Alguns desenvolvedores tendem a usar
sealed
demais; outros não se importam com como a classe seria herdada e com o problema que ela pode causar. Dado esse uso, o problema é semelhante ao que consiste em determinar se os programadores devem usar dois ou quatro espaços para indentação: não há resposta certa.fonte
tl;dr
aqui. Ou é "Não, não existe uma única boa razão" ou "Acho que não há uma boa razão, mas também não sei". . Para mim, "nenhuma resposta definitiva" é uma delas.sealed
é usado para indicar que o escritor da classe não a projetou para ser herdado. Essa é sua única razão.String
e depois a mutação após a verificação de segurança, mas antes da E / S? Acredito que esse tipo de cenário é o motivo pelo qual o JavaString
é marcadofinal
(semelhante asealed
) e aposto que a mesma lógica está subjacente a algumas decisões em C #.Quando uma classe é selada, permite que o código usando esse tipo saiba exatamente com o que está lidando.
Agora em C #
string
ésealed
, você não pode herdar a partir dele, mas vamos supor que por um segundo que isso não era o caso. Se você fez, alguém poderia escrever uma aula como esta:Agora, essa seria uma classe de string que viola todos os tipos de propriedades que os designers
string
sabiam serem verdadeiras. Eles sabiam que oEquals
método seria transitivo, não é. Caramba, esse método equals nem é simétrico, pois uma string pode ser igual a ela, mas não seria igual a essa string. Tenho certeza de que existem todos os tipos de programadores que escreveram código sob a suposição de que oToString
métodostring
não lançará uma exceção para valores não nulos. Agora você poderá violar essa suposição.Há várias outras classes pelas quais poderíamos fazer isso e todos os tipos de outras suposições que poderíamos violar. Se você remover o recurso de idioma para
sealed
agora os designers de idiomas precisam começar a contabilizar coisas como essa em todo o código de sua biblioteca. Eles precisam executar todos os tipos de verificações para garantir que os tipos estendidos dos tipos que desejam usar sejam gravados "corretamente", o que pode ser uma coisa muito cara a se fazer (tanto em tempo de desenvolvimento quanto em tempo de execução). Ao poder selar uma classe, eles podem garantir que isso não seja possível e que quaisquer afirmações feitas sobre os detalhes de implementação de seus próprios tipos não possam ser violadas, exceto para aqueles que foram projetados intencionalmente para serem estendidos. Para aqueles tipos projetados para serem estendidos, você encontrará que o código do idioma precisa ser muito mais defensivo sobre como ele lida com eles.fonte
sealed
recursos incorporados que não estão expostos para uso. Ainda não explica por que usá-lo fora de um escopo tão restrito e, sendo tão estreito quanto o código do compilador , nem explica asealed
existência.string
tipo não é código do compilador. Faz parte do código do idioma. Completamente diferente. De qualquer forma, acabei de escolher um exemplo. Você pode escolher quase todas as classes seladas em todo o BCL e usá-las como exemplo.Enh? Não frequente. Eu só utilizado selado quando eu tenho um tipo imutável (ou alguma outra limitação) que realmente realmente precisa permanecer imutável e não posso herdeiros de confiança para afeções medulares essa exigência sem introduzir sutis, bugs catastropic no sistema.
Usamos herança para coisas que não são padrão. Mesmo que a composição seja preferida à herança (e é), isso não significa que a herança deva ser descartada. Isso significa que a herança tem alguns problemas intrínsecos que ela introduz no design e na manutenção e composição do código e faz a mesma coisa com (em geral) menos problemas. E isso significa que, mesmo que a composição seja favorecida, a herança é boa para certos subconjuntos de problemas.
Não pretendo ser muito mesquinha, mas se você já ouviu falar do SOLID e dos testes de unidade, talvez deva sair de debaixo da rocha e escrever um código. As coisas não são claras, e raramente "sempre faz X" ou "nunca usa Y" ou "Z é o melhor" é o caminho certo (como parece que você se baseia em pontos de vista da sua pergunta). Ao escrever o código, você pode ver casos em que isso
sealed
pode ser bom e casos em que isso causa luto - como qualquer outra troca.fonte
sealed
, por favor?Antes de tudo, você deve ler o post de Eric Lippert sobre por que a Microsoft encerra suas aulas.
http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx
Em segundo lugar, a palavra-chave selada existe para fazer exatamente o que faz - impedir a herança. Existem muitas situações em que você deseja impedir a herança. Considere uma classe da qual você tem uma coleção. Se seu código depende dessa classe funcionando de uma maneira específica, você não deseja que alguém substitua um dos métodos ou propriedades nessa classe e cause falha no aplicativo.
Em terceiro lugar, o uso de selado incentiva a composição sobre a herança, o que é um bom hábito. Usar composição sobre herança significa que você não pode violar o princípio de substituição de Liskov e está seguindo o princípio de Aberto / Fechado.
sealed
é, portanto, uma palavra-chave que ajuda você a seguir dois dos cinco princípios mais importantes da programação oo. Eu diria que o torna um recurso muito valioso.fonte
Penso que esta pergunta não tem resposta objetiva e que tudo depende da sua perspectiva.
Por um lado, algumas pessoas querem que tudo seja "aberto" e o ônus de garantir que tudo esteja funcionando corretamente é da pessoa que estende a aula. É por isso que as classes estão abertas à herança por padrão. E por que os métodos Java são todos virtuais por padrão.
Por outro lado, alguns querem que tudo seja fechado por padrão e oferecem opções para abri-lo. Nesse caso, é o autor da classe base que precisa garantir que tudo o que ele abre "seja aberto" seja seguro de usar de qualquer maneira imaginável. É por isso que em C # todos os métodos são não virtuais por padrão e precisam ser declarados virtuais para serem substituídos. É também para essas pessoas que precisam ser uma maneira de contornar os padrões "abertos". Como tornar a aula selada / final, porque eles vêem alguém que deriva da classe um grande problema para o design de sua própria classe.
fonte
sealed
não posso. Como podemos herdar deles? Tudo o que li é que não podemos. Sempre. Eu não sei. Eu estou indo e voltando sobre esse assunto há 4 meses, desde que ouvi falar pela primeira vez sobre o assunto. E ainda não sei como fazer isso corretamente quando preciso modificar qualquer coisa em uma classe selada. Então, não vejo "opção para abri-lo"!o problema com selado em c # é que é bastante final. Em 7 anos de desenvolvimento comercial em C #, o único lugar que eu já vi usado é nas classes Microsoft .NET, onde sinto que tinha um motivo legítimo para estender uma classe de estrutura para implementar um comportamento ajustado e, como a classe foi selada, não consegui .
É impossível escrever uma classe pensando em todas as formas possíveis de usá-la e decidindo que nenhuma delas é legítima. Da mesma forma, se a segurança da estrutura depende da classe não ser herdada, você tem uma falha no design de segurança.
Portanto, em conclusão, sou firmemente da opinião de que o selo não serve para nenhum propósito útil, e nada que li até agora mudou essa visão.
Eu posso ver que até agora houve três argumentos que justificam selado colocado até agora.
O argumento 1 é que ele elimina uma fonte de erros quando as pessoas herdam das classes e aumentam o acesso aos internos dessa classe sem realmente entender os internos adequadamente.
O argumento 2 é que, se você estender uma classe da Microsoft e, em seguida, a Microsoft implementar uma correção de bug nessa classe, mas sua extensão dependesse desse bug, seu código será quebrado agora, a Microsoft será responsabilizada e a lacuna impedirá que
O argumento 3 é que, como desenvolvedor da classe, é minha escolha se você deseja estendê-la, como parte do meu design da classe.
O argumento 1 parece ser um argumento de que você, o programador final, não sabe como usar meu código. Pode ser esse o caso, mas se você ainda me permite acessar a interface de objetos, ainda é verdade que estou usando seu objeto e fazendo chamadas sem entender como usá-lo, e pode causar tanto dano dessa maneira . Essa é uma justificativa fraca da necessidade de selar.
Eu entendo de onde vem a base factual para arg 2. Em particular, quando a microsoft colocou verificações adicionais do sistema operacional nas chamadas da API do win32 para identificar chamadas malformadas, que melhoraram a estabilidade geral do Windows XP em comparação ao Windows NT, mas a curto prazo, muitos aplicativos foram quebrados quando o Windows XP foi lançado. A Microsoft resolveu isso colocando 'regras' para que essas verificações digam: ignore quando o aplicativo X quebrar essa regra, é um bug conhecido
O argumento 2 é um argumento fraco, porque, na melhor das hipóteses, lacrado não faz diferença, porque se houver um erro na biblioteca .NET, você não terá outra opção a não ser encontrar uma solução alternativa e, quando a correção da Microsoft for lançada, é bem provável significa que o seu trabalho em torno do qual depende o comportamento quebrado agora está quebrado. Independentemente de você ter contornado isso usando herança ou algum outro código adicional do wrapper, quando o código for corrigido, sua solução será quebrada.
O argumento 3 é o único argumento que tem alguma substância para justificar a si próprio. Mas ainda é fraco. Isso simplesmente contraria a reutilização de código.
fonte
It is impossible to write a class thinking about every possible way it could be used
- Essa é precisamente a razão pela qual muitas classes do Framework são seladas ... Isso elimina a necessidade de pensar em todas as formas possíveis de estender a classe.Eu só uso
sealed
palavras-chave em classes que contêm apenas métodos estáticos.fonte
static
?Como é evidente pela minha pergunta, não sou especialista aqui. Mas não vejo razão para
sealed
existir.Parece ter sido feito para ajudar os arquitetos de código a projetar uma interface. Se você deseja escrever uma classe que sairá em estado selvagem e muitas pessoas herdarão dela, modificar qualquer coisa nessa classe pode quebrá-la para muitas pessoas. Então podemos selar e ninguém pode herdar. "Problema resolvido".
Bem, parece "cobrir um ponto e descobrir outro". E nem está resolvendo um problema - está apenas eliminando uma ferramenta : herança. Como qualquer ferramenta, ela tem muitos usos e herdar de uma classe instável é um risco que você corre. Quem correu esse risco deveria saber melhor.
Talvez possa haver uma palavra-chave
stable
, para que as pessoas saibam que é mais seguro herdar dela.fonte
sealed
não seja uma ferramenta útil para os designers de estruturas. Classes em uma estrutura devem ser caixas pretas; você não precisa conhecer os internos de uma turma para poder usá-la adequadamente. No entanto, é exatamente isso que a herança exige.