Esse projeto de OOP é ruim para uma simulação envolvendo interfaces?

13

Estou projetando meu próprio programa OOP para simular vampiros, lobos, humanos e caminhões e estou tentando implementar meu próprio entendimento limitado de interfaces.

( Ainda estou abstraindo aqui e ainda não tenho implementação de código, então é uma questão de design de OOP ... eu acho!)

Estou certo ao procurar 'comportamento comum' entre essas classes e implementá-las como interfaces ?

Por exemplo, vampiros e lobos mordem ... então eu deveria ter uma interface de mordida?

public class Vampire : Villain, IBite, IMove, IAttack

Da mesma forma para caminhões ...

public class Truck : Vehicle, IMove

E para os humanos ...

public class Man : Human, IMove, IDead

O meu pensamento está aqui? (Agradeço sua ajuda)

user3396486
fonte
14
Animais, vegetais e minerais raramente são bons exemplos para implementações de aplicativos. Implementações reais são geralmente mais abstrato, como IEnumerable, IEquatable, etc.
Robert Harvey
6
Você tem uma única menção do que seus objetos estão prestes a fazer no seu software ("mordida"). Normalmente, o software é projetado para fazer alguma coisa, basear um modelo de objeto apenas nas características não leva a lugar algum.
tofro
@tofro Minha intenção era que o IBite contivesse vários métodos que implementassem comportamentos relacionados a: (1) redução do nível de 'vida / energia' de outro (2) aparecimento ou invocação de gráficos de 'sangue' e (3) atualização de estática da simulação dados (como NoOfBites). Eu acho que posso entender que uma interface é melhor usada para implementar uma variedade de comportamentos de método.
user3396486
2
As classes Humano, Vampiro e Veículo já não implementam a interface IMove? Por que você precisa fazer as subclasses implementá-lo de maneira muito explícita?
Pierre Arlaud
Todas essas interfaces são realmente necessárias? Felizmente, em Python, você não precisa de nada disso, o que foi uma mudança realmente refrescante (minha primeira linguagem foi o Object Pascal). Métodos virtuais também podem ser uma solução melhor em alguns casos.
Ajasja

Respostas:

33

Em geral, você deseja ter interfaces para características comuns da sua classe.

Eu concordo parcialmente com @Robert Harvey nos comentários, que disseram que geralmente as interfaces representam características mais abstratas das classes. No entanto, acho que a partir de exemplos mais concretos é uma boa maneira de começar a pensar abstrato.

Embora seu exemplo seja tecnicamente correto (ou seja, sim, vampiros e lobos mordem, para que você possa ter uma interface para isso), há uma questão de relevância. Cada objeto tem milhares de características (por exemplo, os animais podem ter pêlos, podem nadar, podem escalar árvores etc.). Você fará uma interface para todos eles? Muito menos provável.

Você geralmente deseja que interfaces para coisas que fazem sentido sejam agrupadas em um aplicativo como um todo. Por exemplo, se você estiver criando um jogo, poderá ter uma matriz de objetos IMove e atualizar sua posição. Se você não quiser fazer isso, ter a interface IMove é bastante inútil.

O ponto é, não exagere na engenharia. Você precisa pensar em como vai usar essa interface, e 2 classes com um método em comum não são um motivo suficientemente bom para criar uma interface.

Paul92
fonte
1
Eu certamente espero que cada objeto não tenha milhares de atributos.
gardenhead 29/09/16
4
Atributos não como em atributos opcionais, mas em atributos / características gramaticais (coisas como enumeráveis, comparáveis ​​etc.): D. Má escolha de palavras.
Paul92
3
Vale ressaltar que as interfaces úteis são as que você usará. Por exemplo, IBitenão é particularmente útil, mas você pode querer IAttacktrabalhar com todas as coisas que fazem ataques, ou um IUpdatepara executar as atualizações de tudo, ou um IPhysicsEnabledpara aplicar a física a eles etc.
anaximander
1
Esta resposta levanta alguns pontos muito bons. O parágrafo final resume muito bem; pelo menos da melhor maneira possível com o nível de detalhe fornecido.
Lightness Races com Monica
1
agrupar métodos comuns se encaixa mais nas classes abstratas. São feitas interfaces para projetar contratos que devem respeitar aqueles que a implementam, sem agrupar a mesma implementação para alguns objetos.
Walfrat 30/09/16
28

Parece que você está criando várias interfaces de método único . Isso é bom, mas lembre-se de que as interfaces não pertencem às classes que os implementam. Eles pertencem aos clientes que os utilizam. Os clientes decidem se algo precisa ser algo que possa se mover e atacar.

Se eu tiver uma Combatclasse com um fight()método, esse método provavelmente precisará chamar ambos move()e attack()no mesmo objeto. Isso sugere fortemente a necessidade de uma ICombatantinterface que fight()possa chamar move()e attack()através. É mais limpo do que fight()pegar um IAttackobjeto e lançá-lo IMovepara ver se ele também pode se mover.

Isso não significa que você também não pode ter IMove IAttackinterfaces. Eu só espero que você não os esteja fazendo sem que algum cliente precise deles. Por outro lado, se nenhum cliente precisar fazer com que um objeto se mova e ataque, ICombatantnão será necessário.

Essa maneira simples de ver as interfaces geralmente se perde porque as pessoas gostam de seguir exemplos. As primeiras interfaces às quais estamos expostos estão nas bibliotecas. Infelizmente, as bibliotecas não têm idéia do que são seus clientes. Assim, eles podem apenas adivinhar as necessidades de seus clientes. Não é o melhor exemplo a seguir.

candied_orange
fonte
1
Porra, isso é bom. Jogar parece ser realmente uma boa maneira de usar e explicar o POO.
JeffO 29/09/16
6
@JeffO, até que você implemente um jogo razoavelmente grande e perceba que o OOP é uma bagunça e você se sairia melhor com sistemas baseados em componentes ou projetos orientados a dados.
Darkhogg 30/09/16
"As interfaces são de propriedade dos clientes que as usam"
Tibos 30/09/16
1
+1 pela diferença entre bibliotecas e aplicativos, muitas vezes (demais?: /) Leio toneladas de coisas que se encaixam apenas em uma e não na outra.
Walfrat 30/09/16
3

Considere se será comum ter coleções de objetos com diferentes combinações de habilidades e se o código pode querer executar uma ação sobre esses itens, dentro de uma coleção, que o suportam . Nesse caso, e se houver um "comportamento padrão" sensato para objetos que não têm suporte útil para alguma ação, pode ser útil ter interfaces implementadas por uma ampla gama de classes, não apenas aquelas que podem se comportar de maneira útil.

Por exemplo, suponha que apenas alguns tipos de criatura possam ter Woozles, e alguém deseja que essas criaturas tenham uma NumerOfWoozlespropriedade. Se essa propriedade estivesse em uma interface implementada apenas por criaturas que podem ter Woozles, o código que desejasse encontrar o número total de Woozles mantidos por uma coleção de criaturas de tipos mistos teria que dizer algo como:

int total = 0;
foreach (object it in creatures)
{
   IWoozleCountable w = trycast(it, IWoozleCountable);
   if (w != null) total += w.WoozleCount;
}

Se, no entanto, o WoozleCount fosse um membro do Creature / ICreature, mesmo que poucos subtipos substituíssem a implementação padrão do WoozleCount do Creature que sempre retorna zero, o código poderia ser simplificado para:

int total = 0;
foreach (ICreature it in creatures)
   total += it.WoozleCount;

Embora algumas pessoas possam se irritar com a idéia de todas as Criaturas implementarem uma propriedade WoozleCount que seja realmente útil apenas para alguns subtipos, a propriedade seria significativa para todos os tipos, independentemente de ser útil ou não com itens conhecidos por esses tipos, e eu consideraria a interface "pia da cozinha" menos odor de código do que o operador trycast.

supercat
fonte