Ao usar o conceito de polimorfismo, você cria uma hierarquia de classes e, usando a referência dos pais, chama as funções de interface sem saber qual tipo específico tem o objeto. Isso é ótimo. Exemplo:
Você tem uma coleção de animais e solicita a função de todos os animais eat
e não se importa se é um cachorro comendo ou um gato. Mas na mesma hierarquia de classes você tem animais que possuem outros - além de herdados e implementados da classe Animal
, por exemplo makeEggs
, getBackFromTheFreezedState
e assim por diante. Portanto, em alguns casos em que você trabalha, você pode querer saber o tipo específico para chamar comportamentos adicionais.
Por exemplo, no caso, é tempo de manhã e se ele é apenas um animal, então você chamar eat
, caso contrário, se ele é um ser humano, em seguida, chamar primeiro washHands
, getDressed
e só então chamar eat
. Como lidar com esses casos? Polimorfismo morre. Você precisa descobrir o tipo do objeto, que soa como um cheiro de código. Existe uma abordagem comum para lidar com esses casos?
Eater
interface com oeat()
método, então, como cliente, você não se importará que umaHuman
implementação tenha que chamar pela primeira vezwashHands()
egetDressed()
são detalhes da implementação dessa classe. Se, como cliente, você se importa com esse fato, provavelmente não está usando a ferramenta correta para o trabalho.getDressed
antes deleeat
, não é o caso do almoço. Dependendo das circunstâncias,washHands();if !dressed then getDressed();[code to actually eat]
pode ser a melhor maneira de implementar isso para um ser humano. Outra possibilidade é o que acontece se outras coisas exigirem issowashHands
e / ougetDressed
forem chamadas? Suponha que você tenhaleaveForWork
? Pode ser necessário estruturar o fluxo do seu programa para que ele seja chamado muito antes disso.Respostas:
Depende. Infelizmente não há solução genérica. Pense nos seus requisitos e tente descobrir o que essas coisas devem fazer.
Por exemplo, você disse que pela manhã animais diferentes fazem coisas diferentes. Que tal você introduzir um método
getUp()
ouprepareForDay()
ou algo parecido. Depois, você pode continuar com o polimorfismo e deixar que cada animal execute sua rotina matinal.Se você deseja diferenciar os animais, não os armazene indiscriminadamente em uma lista.
Se nada mais funcionar, você pode tentar o Padrão do Visitante , que é uma espécie de hack para permitir um tipo de despacho dinâmico, no qual você pode enviar um visitante que receberá retornos de tipo exato dos animais. Eu enfatizaria, no entanto, que esse deveria ser o último recurso se tudo mais falhar.
fonte
Essa é uma boa pergunta e é o tipo de problema que muitas pessoas tentam entender como usar o OO. Eu acho que a maioria dos desenvolvedores luta com isso. Eu gostaria de poder dizer que a maioria supera isso, mas não tenho certeza de que seja esse o caso. A maioria dos desenvolvedores, na minha experiência, acaba usando pacotes de propriedades pseudo-OO .
Primeiro, deixe-me esclarecer. Isso não é culpa sua. A maneira como o OO é ensinado é altamente falha. O
Animal
exemplo é o principal infrator, a IMO. Basicamente, dizemos, vamos falar sobre objetos, o que eles podem fazer. UmaAnimal
lataeat()
e podespeak()
. Super. Agora crie alguns animais e codifique como eles comem e falam. Agora você sabe OO, certo?O problema é que isso está chegando ao OO na direção errada. Por que existem animais neste programa e por que eles precisam falar e comer?
É difícil pensar em um uso real para um
Animal
tipo. Tenho certeza de que existe, mas vamos discutir algo que acho mais fácil de raciocinar: uma simulação de tráfego. Suponha que queremos modelar o tráfego em vários cenários. Aqui estão algumas coisas básicas que precisamos ter para poder fazer isso.Podemos ir mais fundo com todo tipo de coisas que os pedestres e trens, mas manteremos simples.
Vamos considerar
Vehicle
. De quais recursos o veículo precisa? Ele precisa viajar em uma estrada. Ele precisa ser capaz de parar nos sinais. Ele precisa navegar pelas interseções.Provavelmente é muito simples, mas é um começo. Agora. E quanto a todas as outras coisas que um veículo pode fazer? Eles podem sair de uma estrada e entrar em uma vala. Isso faz parte da simulação? Não. Não precisa disso. Alguns carros e ônibus têm sistemas hidráulicos que permitem saltar ou se ajoelhar, respectivamente. Isso faz parte da simulação? Não. Não precisa disso. A maioria dos carros queima gasolina. Alguns não. A usina faz parte da simulação? Não. Não precisa disso. Tamanho da roda? Não precisa disso. Navegação GPS? Sistema de informação e lazer? Não preciso deles.
Você só precisa definir comportamentos que você vai usar. Para esse fim, acho que geralmente é melhor criar interfaces OO a partir do código que interage com elas. Você começa com uma interface vazia e começa a escrever o código que chama os métodos inexistentes. É assim que você sabe quais métodos você precisa na sua interface. Depois que você fizer isso, comece a definir classes que implementam esses comportamentos. Os comportamentos que não são usados são irrelevantes e não precisam ser definidos.
O ponto principal do OO é que você pode adicionar novas implementações dessas interfaces posteriormente sem alterar o código de chamada. A única maneira de funcionar é se as necessidades do código de chamada determinarem o que ocorre na interface. Não há como definir todos os comportamentos de todas as coisas possíveis que poderiam ser pensadas mais tarde.
fonte
TL; DR:
Pense em uma abstração e métodos que se apliquem a todas as subclasses e cubram tudo o que você precisa.
Vamos primeiro ficar com o seu
eat()
exemplo.É uma propriedade de ser humano que, como pré-condição para comer, os humanos querem lavar as mãos e se vestir antes de comer. Se você quiser alguém para vir até você para comer o pequeno-almoço com você, você não dizer -lhes para lavar as mãos e se vestir, eles fazem isso por conta própria quando você convidá-los, ou eles respondem "Não, eu não posso vir acabou, não lavei as mãos e ainda não estou vestida ".
Voltar ao software:
Como um
Human
exemplo, não vai comer sem as pré-condições, eu teria oHuman
'seat()
método de fazerwashHands()
egetDressed()
se isso não foi feito. Não deve ser seu trabalho comoeat()
interlocutor saber sobre essa peculiaridade. A alternativa do humano teimoso seria lançar uma exceção ("não estou preparado para comer!") Se as condições prévias não fossem cumpridas, deixando-o frustrado, mas pelo menos informado que comer não funcionava.Que tal
makeEggs()
?Eu recomendaria mudar sua maneira de pensar. Você provavelmente deseja executar as tarefas matinais programadas de todos os seres. Novamente, como interlocutor, não deve ser seu trabalho saber quais são os deveres deles. Então, eu recomendaria um
doMorningDuties()
método que todas as classes implementem.fonte
A resposta é bem simples.
Você não precisa lidar com isso, porque não serve para nada. Uma interface geralmente é projetada dependendo de como será usada. Se sua interface não definir lavar as mãos, você não se preocupará com isso como o responsável pela chamada; se o fizesse, o projetaria de maneira diferente.
Por exemplo, no pseudocódigo:
Agora você implementar
IMorningPerformer
paraAnimal
apenas realizar comer, e paraHuman
você também implementá-lo para lavar as mãos e se vestir. Quem chama o método MorningTime pode se importar menos se for humano ou animal. Tudo o que ela quer é a rotina matinal realizada, que cada objeto faz admiravelmente graças ao OO.Ou faz?
Por que está assumindo isso? Eu acho que isso pode ser uma suposição errada.
Sim, geralmente é resolvido com hierarquia de classe ou interface cuidadosamente projetada. Observe que no exemplo acima não há nada que contradiga o seu exemplo, como você o deu, mas provavelmente você se sentirá insatisfeito, porque fez mais algumas suposições que não escreveu na pergunta no momento em que escrevia. , e essas suposições provavelmente são violadas.
É possível ir a uma toca de coelho apertando suas suposições e modificando a resposta para ainda satisfazê-las, mas acho que não seria útil.
Projetar boas hierarquias de classe é difícil e requer muita percepção do domínio de negócios. Para domínios complexos, é feita uma repetição de duas, três ou mais iterações, pois elas refinam o entendimento de como as diferentes entidades em seu domínio de negócios interagem, até chegarem a um modelo adequado.
É aí que faltam exemplos simplistas de animais. Queremos ensinar de maneira simples, mas o problema que estamos tentando resolver não é óbvio até que você se aprofunde, ou seja, tenha considerações e domínios mais complexos.
fonte