Uma classe deve saber sobre suas subclasses?

24

Uma classe deve saber sobre suas subclasses? Uma classe deve fazer algo específico para uma determinada subclasse, por exemplo?

Meus instintos me dizem que esse é um projeto ruim, parece algum tipo de antipadrão.

m4design
fonte
6
Geralmente não, mas em casos especiais é útil.
CodesInChaos
Ele é um anti-padrão, um bem conhecido um chamado: "substituição de Liskov" ou às vezes apenas LSP, ler sobre ele em en.wikipedia.org/wiki/Liskov_substitution_principle
Jimmy Hoffa
5
@ JimmyHoffa - "Princípio de substituição de Liskov" não é o nome de um antipadrão. Um antipadrão pode violar o LSP, mas não é certo que isso seja verdade aqui. É possível que, mesmo que um tipo conheça seus subtipos, os subtipos ainda possam ser substituídos por seus pais por contravariância e covariância.
Matthew Flynn
4
@MatthewFlynn Talvez eu esteja usando o LSP um pouco vagamente, mas, na minha definição, não é o fato de que todos eles cumprem os requisitos um do outro que estejam em conformidade com o LSP, é um requisito que eles tenham uma dissociação de modo que não apenas concordem, mas você pode implementar outra subclasse que não viole o LSP. Se a hierarquia é autoconsciente, uma implementação externa não pode atender ao LSP sem alterar a classe base. Talvez isso não seja estritamente LSP, mas é muito próximo. e sim, eu deveria ter dito LSP violação é o anti-padrão
Jimmy Hoffa
2
Nas poucas vezes em que encontrei isso, refatorei esse conhecimento para um método que poderia ser substituído em uma subclasse (forçado por torná-lo abstrato ou virtual puro ou fornecendo uma implementação padrão sensível que poderia ser substituída).

Respostas:

32

A resposta implícita no conceito de classe é "não".

Qualquer ação, dado ou relação que você esteja manipulando faz parte de todas as subclasses - então ele deve ser tratado na superclasse sem verificar o tipo real. Ou aplica-se apenas a algumas subclasses - então você teria que executar verificações de tipo de tempo de execução para fazer a coisa certa, a superclasse teria que ser alterada sempre que alguém herda dela (ou pode fazer silenciosamente a coisa errada), alterações nas classes derivadas podem quebrar a superclasse inalterada etc.

Em suma, você obtém uma série de consequências ruins que geralmente são ruins o suficiente para rejeitar tais soluções imediatamente. Se várias de suas subclasses fazem a mesma coisa e você deseja evitar a duplicação de código (praticamente sempre uma coisa boa), uma solução melhor é introduzir uma classe de nível médio a partir da qual todas essas subclasses podem herdar o código.

Kilian Foth
fonte
4
Uma exceção a esta regra: A documentação da classe deve listar suas subclasses locais em sua própria biblioteca.
Kieveli 26/11
3
@Kieveli ... desde que essa documentação seja mantida atualizada.
Mouviciel 26/11
14
Qualquer instrumento de documentação utilizável deve ser capaz de desenhar árvores de herança ...
Johannes
13
Não acho que seja uma exceção. A turma ainda não sabe de nada. A documentação sabe.
Honra Brabec #
4
@Kieveli uhh eu discordo completamente .
Jimmy Hoffa
16

Não apenas não deveria saber, como simplesmente não pode ! Normalmente, uma classe pode ser estendida a qualquer hora, em qualquer lugar. Pode ser estendido por classes que nem existiam quando foram escritas.

Alguns idiomas permitem que as classes extensivas sejam controladas pela superclasse. No Scala, uma classe pode ser marcada como sealed, o que significa que só pode ser estendida por outras classes na mesma unidade de compilação (arquivo de origem). No entanto, a menos que essas subclasses também sejam sealedou final, as subclasses poderão ser estendidas por outras classes.

No Scala, isso é usado para modelar tipos de dados algébricos fechados, de modo que o Listtipo canônico de Haskell :

data List a = Nil | Cons a (List a)

pode ser modelado no Scala assim:

sealed trait List[+A]

case object Nil extends List[Nothing]

final case class Cons[+A] extends List[A]

E você pode garantir que apenas essas duas sub- "classes" existam porque Listsão sealede, portanto, não podem ser estendidas fora do arquivo, Conssão finale, portanto, não podem ser estendidas de todo e Nilé uma objectque não pode ser estendida de qualquer maneira.

Mas este é um caso de uso específico (modelagem de tipos de dados algébricos por herança) e, mesmo nesse caso, a superclasse não conhece realmente suas subclasses. É mais uma garantia para o usuário do Listtipo que, se ele fizer uma discriminação de casos Nile Cons, não haverá outra alternativa aparecendo nas suas costas.

Jörg W Mittag
fonte
O que funciona em muitos idiomas é adicionar algum tipo de abstract internalmembro.
CodesInChaos
4

A resposta simples é não.

Torna o código frágil e anula dois princípios básicos da programação orientada a objetos.

  • Princípio da substituição
  • Princípio Open Close
Sajad Deyargaroo
fonte
1

Sim as vezes. Por exemplo, quando existe um número limitado de subclasses. Um padrão de visitante é uma ilustração da utilidade dessa abordagem.

Exemplo: todos os nós da árvore de sintaxe abstrata (AST) de alguma gramática bem definida podem ser herdados de uma única Nodeclasse implementando um padrão de visitante para manipular todos os tipos de nós.

lorus
fonte
9
Não, não e não. Isso não é útil e nunca é uma boa solução.
Sulthan
@Sulthan Eu trabalhei com ASTs fora da sala de aula e acredito que o lorus não entende realmente a pergunta. Um visitante não é um tipo de nó em um AST, é um objeto separado. Ele pode e deve saber sobre os objetos gramaticais. Mas um nó AST de alto nível não deve.
11
@JohnGaughan O termo "sabe" me confunde. A Nodeclasse base , é claro, não contém nenhuma referência direta a suas subclasses. Mas ele contém um acceptmétodo com um Visitorparâmetro E Visitorcontém um visitmétodo para cada subclasse. Portanto, apesar de Nodenão ter referências diretas a suas subclasses, ele "sabe" indiretamente sobre elas, através da Visitorinterface. Todos eles se uniram através dele.
Lorus
11
@Sulthan Nunca diga nunca. O tipo de opção é um exemplo válido do caso em que a supreclasse conhece o descendente. Mas, como Jorg disse, seria marcado como selado.
Pavel Voronin
Então estamos concordando: um visitante precisa saber o que está visitando.
1

Se eu escrever um componente para uma empresa e mais tarde, depois que eu for embora, alguém o estenderá para seus próprios propósitos, devo ser informado sobre isso?

Não!

O mesmo com as classes. Confie nos seus instintos.

Daniel Hollinrake
fonte
Seguro desemprego???
sixtyfootersdude
Você tem sessenta pés de altura, então não discutirei com você :-) No entanto, espero que meu significado seja depois que eu for embora e arrumar outro emprego.
Daniel Hollinrake