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.
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.
Respostas:
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.
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 sealed
ou 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 List
tipo 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 List
são sealed
e, portanto, não podem ser estendidas fora do arquivo, Cons
são final
e, portanto, não podem ser estendidas de todo e Nil
é uma object
que 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 List
tipo que, se ele fizer uma discriminação de casos Nil
e Cons
, não haverá outra alternativa aparecendo nas suas costas.
abstract internal
membro.
A resposta simples é não.
Torna o código frágil e anula dois princípios básicos da programação orientada a objetos.
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 Node
classe implementando um padrão de visitante para manipular todos os tipos de nós.
Node
classe base , é claro, não contém nenhuma referência direta a suas subclasses. Mas ele contém um accept
método com um Visitor
parâmetro E Visitor
contém um visit
método para cada subclasse. Portanto, apesar de Node
não ter referências diretas a suas subclasses, ele "sabe" indiretamente sobre elas, através da Visitor
interface. Todos eles se uniram através dele.
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.