Suponha que você tenha o seguinte:
+--------+ +------+
| Animal | | Food |
+-+------+ +----+-+
^ ^
| |
| |
+------+ +-------+
| Deer | | Grass |
+------+ +-------+
Deer
herda de Animal
e Grass
herda de Food
.
Por enquanto, tudo bem. Animal
objetos podem comer Food
objetos.
Agora vamos misturar um pouco. Vamos adicionar um Lion
que herda de Animal
.
+--------+ +------+
| Animal | | Food |
+-+-----++ +----+-+
^ ^ ^
| | |
| | |
+------+ +------+ +-------+
| Deer | | Lion | | Grass |
+------+ +------+ +-------+
Agora temos um problema porque Lion
podemos comer os dois Deer
e Grass
, mas Deer
não Food
é Animal
.
Sem o uso de herança múltipla e o design orientado a objetos, como você resolve esse problema?
FYI: Usei http://www.asciiflow.com para criar os diagramas ASCII.
object-oriented
object-oriented-design
Michael Irey
fonte
fonte
IHuntable
, Sheep e Cow sãoIHerdable
(controláveis por humanos), e o Lion implementa apenas o IAnimal, o que não implica em nenhuma dessas interfaces. O AOE3 suporta a consulta do conjunto de interfaces suportadas por um objeto específico (semelhante ainstanceof
) que permite que um programa consulte suas capacidades.Respostas:
Relações IS A = Herança
Leão é um animal
TEM UM relacionamento = Composição
Carro tem uma roda
PODE FAZER relacionamentos = Interfaces
Eu posso comer
fonte
ICanBeEaten
ouIEdible
OO é apenas uma metáfora que se molda ao mundo real. Mas as metáforas só vão tão longe.
Normalmente, não há maneira correta de modelar algo no OO. Existe uma maneira correta de fazer isso para um problema específico em um domínio específico e você não deve esperar que funcione bem se alterar o problema, mesmo que os objetos do domínio sejam os mesmos.
Eu acho que este é um equívoco comum mais Comp. Eng. os alunos têm em seus primeiros anos. OO não é uma solução universal, apenas uma ferramenta decente para algum tipo de problema que pode modelar seu domínio razoavelmente bem.
Não respondi à pergunta, precisamente porque nos falta informações sobre o domínio. Mas com o exposto acima, você poderá criar algo que atenda às suas necessidades.
fonte
Você deseja dividir ainda mais os animais em suas subclasses (ou pelo menos na medida em que faz sentido para o que está fazendo). Como você trabalha com animais básicos e dois tipos de alimentos (plantas e carne), faz sentido usar carnívoros e herbívoros para definir melhor um animal e mantê-lo separado. Aqui está o que eu desenhei para você.
Como você pode ver, ambos expõem um método de comer, mas o que comem muda. Agora, o Leão pode matar um cervo, o cervo pode morrer e devolver o DeerMeat, e a pergunta original do OP sobre como permitir que um leão coma um cervo, mas a grama não é respondida sem a engenharia de todo um ecossistema.
Obviamente, isso se torna muito rápido porque um cervo também pode ser considerado um tipo de carne, mas, para simplificar, eu criaria um método chamado kill () sob o cervo, que retorna uma carne de cervo e o colocaria como um classe de concreto que estende a carne.
fonte
Eat(Plant p)
eEat(Meat m)
ambos violam o LSP.Meu design seria assim:
Como os animais implementam o IMeat e o Deer é um animal (Herbívoro), o Lion, que é um animal (Carnívoro) que pode comer o IMeat também pode comer o Deer.
O cervo é um herbívoro, por isso pode comer capim porque implementa IVegetable.
Carnívoros não podem comer IVegeable e Herbívoros não podem comer IMeat.
fonte
Os alimentos que um animal pode ingerir não formam uma hierarquia; nesse caso, a natureza falhou indesculpável em se adaptar à modelagem simples orientada a objetos (observe que, mesmo se o fizesse, o animal teria que herdar dos alimentos, pois é alimento).
O conhecimento de quais alimentos um animal pode ingerir não pode viver inteiramente com nenhuma das classes, portanto, simplesmente ter uma referência a algum membro da hierarquia alimentar não é suficiente para dizer o que você pode comer.
É um relacionamento de muitos para muitos. Isso significa que toda vez que você adiciona um animal, você precisa descobrir o que ele pode comer, e toda vez que você adiciona um alimento, você precisa descobrir o que pode comê-lo. Se existe mais estrutura para explorar, depende de quais animais e alimentos você estiver modelando.
A herança múltipla também não resolve muito bem isso. Você precisa de algum tipo de coleção de coisas que um animal possa comer ou de animais que possam comer.
fonte
Vou abordar o problema de outro lado: OOP é sobre comportamento. No seu caso,
Grass
tem algum comportamento para ser filhoFood
? Portanto, no seu caso, não haveráGrass
classe ou, pelo menos, não será herdadoFood
. Além disso, se você precisar aplicar quem pode comer o que em tempo de compilação, é questionável se você precisa deAnimal
abstração. Além disso, não é raro ver carnívoros comendo grama , embora não para sustento.Então, eu projetaria isso como (não vou me incomodar com a arte ASCI):
IEdible
com propriedadeType
, que é enum de carne, planta, carcaça, etc. (isso não muda frequentemente e não possui nenhum comportamento específico, portanto, não há necessidade de modelá-lo como classe hiearchy).Animal
com métodosCanEat(IEdible food)
eEat(IEdible food)
, que são lógicos. Então, animais específicos podem verificar sempre que podem comer alimentos em determinadas circunstâncias e depois comer esses alimentos para obter sustento / fazer outra coisa. Além disso, eu modelaria as classes Carnívora, Herbívora e Onívora como padrão de estratégia do que como parte da hierarquia animal.fonte
TL; DR: design ou modelo com um contexto.
Penso que a sua pergunta é difícil porque falta contexto do problema real que você está tentando resolver. Você tem alguns modelos e alguns relacionamentos, mas não possui a estrutura na qual ele precisa trabalhar. Sem contexto, modelagem e metáforas não funcionam bem, deixando a porta aberta para múltiplas interpretações.
Eu acho mais produtivo focar em como os dados serão consumidos. Depois de ter o padrão de uso de dados, é mais fácil trabalhar de volta para o que devem ser os modelos e os relacionamentos.
Por exemplo, requisitos mais detalhados exigirão diferentes relacionamentos com objetos:
Animals
eat
não-Food
likeGastroliths
Chocolate
comoPoison
paraDogs
, mas não paraHumans
Se começarmos o exercício de como modelar o relacionamento simples apresentado, a Interface Alimentar pode ser a melhor; e se essa é a soma total de como os relacionamentos no sistema estão bem. No entanto, apenas alguns requisitos ou relacionamentos adicionais podem afetar amplamente os modelos e relacionamentos que funcionaram no caso mais simples.
fonte
Abordagem de composição por herança do ECS:
Pseudo-código:
Nature
é umsystem
loop que percorre essas entidades, procurando quais componentes eles possuem por meio de uma função de consulta generalizada.Nature
fará com que entidades com fome de carne atacem outras entidades que tenham carne como alimento usando suas armas, a menos que tenham afinidade com essa entidade. Se o ataque for bem-sucedido, a entidade se alimentará da vítima; nesse momento, a vítima se tornará um cadáver privado de carne.Nature
fará com que entidades com fome de plantas se alimentem de entidades que têm plantas como alimento, desde que existam.Talvez desejemos estender
Grass
a necessidade de luz solar e água e queremos introduzir luz solar e água em nosso mundo. No entanto,Grass
não pode procurá-las diretamente, pois não temmobility
.Animals
também pode precisar de água, mas pode procurá-la ativamente, pois precisamobility
. É muito fácil continuar estendendo e alterando esse modelo sem quebras em cascata de todo o design, pois apenas adicionamos novos componentes e estendemos o comportamento de nossos sistemas (ou o número de sistemas).fonte
Como a maioria das coisas, depende .
Depende do que você vê 'esse problema'.
Se você estiver perguntando sobre o problema geral de implementação, a resposta dependerá dos recursos do seu ambiente. As interfaces IFood e IAnimal podem funcionar, com uma subclasse EdibleAnimal implementando as duas interfaces. Se o seu ambiente não suportar interfaces, basta tornar o Animal herdado do Food.
Se você está perguntando sobre esse problema específico de design, basta fazer o Animal herdar do Food. É a coisa mais simples que poderia funcionar.
Se você está perguntando sobre esses conceitos de design, a resposta depende fortemente do que você pretende fazer com o modelo. Se for para um videogame cachorro-comer-cachorro ou mesmo um aplicativo para rastrear horários de alimentação em um zoológico, pode ser o suficiente para funcionar. Se é para um modelo conceitual de padrões comportamentais de animais, provavelmente é um pouco superficial.
fonte
A herança deve ser usada para algo que sempre é outra coisa e que não pode mudar. Grama nem sempre é comida. Por exemplo, eu não como grama.
A grama desempenha o papel de alimento para certos animais.
fonte
Você acabou de encontrar a limitação básica do OO.
OO funciona bem com estruturas hierárquicas. Mas uma vez que você se afasta de hierarquias estritas, a abstração não funciona tão bem.
Eu sei tudo sobre composições de metamorfose, etc., que são usadas para contornar essas limitações, mas são desajeitadas e, mais importante, levam a códigos obscuros e difíceis de seguir.
Os bancos de dados relacionais foram inventados principalmente para se livrar das limitações de estruturas hierárquicas estritas.
Para dar o seu exemplo, a grama também pode ser um material de construção, uma matéria-prima para papel, um material de vestuário, uma erva daninha ou uma colheita.
Um cervo pode ser um animal de estimação, gado, um animal de zoológico ou uma espécie protegida.
Um leão também pode ser um animal de zoológico ou uma espécie protegida.
A vida não é simples.
fonte
Que problema? O que esse sistema faz? Até você responder, não tenho idéia de quais classes podem ser necessárias. Você está tentando modelar uma ecologia, com carnívoros, herbívoros e plantas, projetando populações de espécies no futuro? Você está tentando fazer o computador executar 20 perguntas?
É uma perda de tempo começar o design antes que quaisquer casos de uso sejam definidos. Vi isso levar a extremos ridículos quando uma equipe de cerca de dez começou a produzir um modelo OO de uma companhia aérea usando software através de fotos. Eles trabalharam por dois anos modelando sem nenhum problema comercial real em mente. Finalmente, o cliente se cansou de esperar e pediu à equipe que resolvesse um problema real. Toda essa modelagem foi completamente inútil.
fonte