对于涉及接口的仿真来说,这是糟糕的OOP设计吗?


13

我正在设计自己的小OOP程序来模拟吸血鬼,狼,人类和卡车,并试图实现自己对接口的有限理解。

我在这里仍然是抽象的,还没有代码实现,所以这是一个OOP设计的问题……我想!)

我在这些类之间寻找“共同行为”并将其实现为接口是否正确?

例如,吸血鬼和狼咬人……那么我应该有一个咬人界面吗?

public class Vampire : Villain, IBite, IMove, IAttack

同样对于卡车...

public class Truck : Vehicle, IMove

对于人类...

public class Man : Human, IMove, IDead

我的想法在这里吗?(感谢您的帮助)


14
动物,蔬菜和矿物质很少是应用程序实现的良好示例。实际实现一般都比较抽象,如IEnumerableIEquatable
罗伯特·哈维·

6
仅需提及您的对象在软件中将要做什么(“咬”)。通常软件设计为执行某些操作,将对象模型仅基于特征是无处不在的。
tofro

@tofro我的意图是IBite将包含多种实现以下方面行为的方法:(1)降低他人的“生命/能量”水平(2)“血液”图形的出现或调用,以及(3)更新模拟静态数据数据(例如NoOfBites)。我想我可以理解,接口最好用于实现一系列方法行为。
user3396486

2
人员,吸血鬼和车辆类是否已经实现IMove接口?为什么需要让子类过于明确地实现它?
皮埃尔·阿洛德

所有这些接口真的必要吗?幸运的是,在Python中您不需要任何这些东西,ehich确实是一个令人耳目一新的更改(我的第一种语言是Object Pascal)。同样,在某些情况下,虚拟方法可能是更好的解决方案。
2016年

Answers:


33

通常,您希望具有针对事务的共同特征的接口。

我在评论中与@Robert Harvey达成了半个共识,他说通常接口代表类的更多抽象特征。尽管如此,我发现从更具体的例子开始是一种开始思考抽象的好方法。

尽管您的示例在技术上是正确的(例如,是的,吸血鬼和狼咬人,所以您可以为此设置接口),但仍然存在相关性问题。每个对象都有数千个特征(例如,动物可能有皮毛,会游泳,会爬树等)。您会为所有这些接口创建界面吗?可能性很小。

通常,您需要将有意义的接口归入整个应用程序中。例如,如果要构建游戏,则可以具有IMove对象数组并更新它们的位置。如果您不想这样做,拥有IMove界面将毫无用处。

重点是,不要过度工程师。您需要考虑如何使用该接口,并且两个具有一个共同方法的类不足以创建一个接口。


1
我当然希望每个对象都没有成千上万个属性。
gardenhead's

4
属性不是oop属性,而是语法属性/特征(诸如可枚举,可比较等):D。单词选择错误。
Paul92

3
值得注意的是,有用的接口就是您将要使用的接口。例如,IBite是不是特别有用,但你可能想IAttack这样你就可以工作在所有使攻击的事情,或者IUpdate这样你就可以一切运行更新,或IPhysicsEnabled使你可以应用物理学他们,等等
阿那克西曼德

1
这个答案提出了一些非常好的观点。最后一段总结得很好。至少与您提供的详细程度一样好。
Lightness Races in Orbit

1
分组通用方法更适合抽象类。接口是为设计合同而设计的,必须尊重实现它们的人员,而不是将某些对象的同一实现分组。
Walfrat

28

看起来您正在创建一堆单一方法接口。从表面上看这很好,但是请记住,接口并不归于实现它们的类所拥有。它们归使用它们的客户所有。客户决定是否需要某种可以移动和攻击的东西。

如果我有一Combat类与fight()方法,该方法可能有需要调用都move()attack()同一个对象。这强烈表明需要一个可以调用并通过的ICombatant接口。这比拿一个物体然后将其投射以查看其是否也可以移动更为清洁。fight()move()attack()fight()IAttackIMove

这并不意味着您也不能拥有IMove IAttack接口。我只是希望您不要在没有某些客户需要它们的情况下制造它们。相反,如果没有客户端需要移动和攻击对象,则ICombatant不需要。

由于人们喜欢以下示例,因此通常看不到这种查看界面的简单方法。我们接触的第一个接口在库中。不幸的是,图书馆不知道他们的客户是什么。因此,他们只能猜测客户的需求。不是最好的例子。


1
该死的,这很好。游戏似乎是使用和解释OOP的一种非常好的方法。
JeffO

6
@JeffO,直到您真正实现一个相当大的游戏并意识到OOP是一团糟,您最好使用基于组件的系统或面向数据的设计。
Darkhogg

“接口归使用它们的客户端所有”
Tibos


1
对于库和应用程序之间的差异+1,我经常(太多了:: /)读了很多只适合于一种而不适合另一种的东西。
Walfrat

3

考虑拥有具有不同能力组合的对象的集合是否常见,以及代码是否希望对集合中支持它的那些项目执行操作。如果是这样,并且对于没有某些动作有用支持的对象,如果存在合理的“默认行为”,则由广泛的类(不仅是那些可以有效发挥作用的类)实现接口可能会有所帮助。

例如,假设只有少数几种生物可以拥有Woozles,而一个人希望此类生物具有NumerOfWoozles属性。如果这样的属性存在于仅由可以拥有Woozles的生物实现的接口中,那么想要查找混合类型的生物集合所持有的Woozles总数的代码将必须像这样:

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

但是,如果WoozleCount是Creature / ICreature的成员,即使很少的子类型将覆盖Creature的默认WoozleCount实现(始终返回零),则代码也可以简化为:

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

尽管有些人可能会对每个Creature实现一个WoozleCount属性(实际上仅对某些子类型有用)的想法感到恼火,但该属性对所有类型都有意义,无论是否适用于已知的那些类型的项目,而且我认为“厨房水槽”界面比trycast运算符具有更少的代码味道。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.