【问题标题】:Interfaces without methods vs fields没有方法的接口与字段
【发布时间】:2017-10-23 05:00:49
【问题描述】:

我有两个类,LivingCreature 和继承自它的 Animal。

我想为每个现有的 Animal 实现一个类,但由于不同的动物共享许多特性,我希望能够将 Animals 分为几个类别: 飞行、步行、游泳、食肉、素食等等……

每只动物都可以居住在几个地方,甚至来自同一个区域,例如飞行、步行和食肉动物。

此外,每个类别可能包含几个独特的属性,例如飞行动物应该包括速度和进食类型(例如它是坐在树上采摘蠕虫,还是“袭击”地球并用腿采摘动物)

我绝对要避免的第一件事是为每个具体的动物实现保存一组不同的字段。

现在,由于有些类别只是二元的(食肉动物或素食主义者),而有些绝对不是,我想知道实现它的正确方法是什么。

我倾向于使用每个类别的接口,即使它们不包含任何方法,但我遇到了冲突:

  • 为这样的简单用途保留一个接口看起来很奇怪,比如 isMeatEating 包含一个布尔字段。
  • 另一方面,将多个类别作为 Animal 的字段,而将其他几个类别作为接口实现,这听起来非常错误且令人困惑。

在设计方面,这里的正确/最佳选择是什么? (也许有一个 与此用例匹配的设计模式)

【问题讨论】:

  • 作为OOP的观点,你会使用抽象类,但是Java拒绝菱形关系,你需要做一些选择来定义最逻辑的继承结构,其余的可能使用关系。在 Java 8 中,您可以在接口中添加默认实现,虽然并不完美,但在某些情况下可以解决问题(也请注意这种用法)
  • 你可能想检查这个Composition over inheritance
  • 如果他们没有方法,你只需要看看他们是否有它,只需使用一个属性。
  • @YthioCsi 看起来主题与我的问题相匹配,但问题是通过使用这种范式,Animal 类将拥有大量字段(因为我需要字段而不是方法),因此每个继承类将包含所有这些字段,这让我回到了最幼稚的实现。也许更好的想法是让 Animal 类几乎是裸露的,并为每个可能的类别创建一个继承类,然后使用多重继承?
  • 首先让我们通过将飞行、步行、游泳等相关类别归为一组,将食肉动物、素食归为另一组来组织信息。由于每个组只能保存特定的正确值集,这意味着它们实际上是枚举。所以我们会有MovementType Enum 和FoodType Enum。因此,您可以将这些枚举仅用于 Animal 类中的属性。

标签: java oop design-patterns


【解决方案1】:

您似乎描述的模式是trait 或mixin。

混合是一种允许程序员将一些代码注入类的语言概念。 Mixin 编程是一种软件开发风格,其中功能单元在一个类中创建,然后与其他类混合。

有些语言,比如 Scala,有built-in support for these concepts。

在 Java 8 中,在默认方法的帮助下,we can do traits 在某种程度上是这样说的,因为这就是 not the reason 设计默认方法的原因。

现在,如果没有完整的工作细节,很难就你的设计提出想法,但我会尝试举例说明我如何从我的角度看待它。

让我们专注于饮食特征:肉食动物与草食动物。食肉动物吃其他动物,食草动物吃植物或植物来源的东西,杂食动物可能两者都吃。动物和植物都是可食用的材料。

interface Edible {
   Collection<Nutrients> getNutrients();
}

interface Vegetal extends Edible {}
interface Animal extends Edible{}

所有动物都吃东西,所以我们可以将动物定义为:

interface Animal<T extends Edible> extends Edible {

    Stomach getStomach();
    Collection<Bolus> masticate(T food);

    default void eat(T food) {
        Objects.requiresNonNull(food, "The food must not be null");
        for(Bolus bolus : this.masticate(food)) {
            this.getStomach().ingest(bolus);
        }
    }
}

不要告诉我Stomach 和Bolus 的详细信息,让我们假设所有动物都有一个胃和一种咀嚼食物的方法,将其变成丸剂并将其摄入胃中。

现在,我们终于可以定义:

interface Carnivore extends Animal<Animal> { }
interface Herbivore extends Animal<Vegetal> { }
interface Omnivore extends Animal<Edible> { }

现在你可以定义一个

class Chicken implements Herbivore {}
class Lion implements Carnivore {}
class Bear implements Omnivore {}

现在Chicken 只能吃Vegetal 类型的东西,Lion 只能吃Animal 类型的东西,而Bear 可以吃任何Edible 类型的东西。

我在接口 Animal 的默认方法上添加的细节越多,我需要在这些类中实现的细节就越少,这也许是实现您想要的一种方式。

我知道我可能无法回答您的问题,但我希望我至少可以为您提供一些想法,让您可以继续调查和试验理想的解决方案。

【讨论】:

    猜你喜欢
    • 2017-10-20
    • 2014-01-19
    • 2014-08-26
    • 1970-01-01
    • 2014-06-02
    • 2012-05-01
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    相关资源
    最近更新 更多