具有接口和抽象的属性继承 类 - Java

Attribute Inheritance with Interfaces and Abstract Classes - Java

好吧,所以我正在尝试为此寻找最佳结构。我想知道在这些情况下什么是最佳实践并且总体上最有效。

问题

假设我正在创造一个小世界。这个世界由不同类型的鸟组成,所以我创建了一只鸟 class 作为所有鸟的 parent。

public abstract class Bird {
  //stuff all birds have in common
}

现在我想创造不同类型的鸟。让我们创建一个 Penguin 和一个 Goose class 并让它们扩展 Bird。

public class Penguin extends Bird{
    //penguin stuff
}

public class Goose extends Bird{
    //goose stuff
}

到目前为止一切顺利,但现在需要扳手了。让我们赋予鸟类飞翔的能力。 惊喜,不是所有的鸟都能飞——比如企鹅!因此,让我们接近我们的选择..

在 Bird 内部创建一个 fly 方法 class 是不可能的(不是所有的鸟都会飞)。另一种臭味的方法是有多个 parent classes,例如 BirdsThatFly,因为这会变得非常混乱,尤其是当其他 birdly 属性被添加到组合中时.

选项 1

接口

理论上听起来不错的一个可行选择是使用接口。例如,我可以有一个 Fly 界面。

public interface Fly {
    public void fly();
}

这将允许我仅在可以飞行的 Bird child classes 上实现 Fly。示例..

public class Goose extends Bird implements Fly {
    @Override
    public void fly() {
        //fly!
    }
}

这看起来很干净,并且将 fly 方法强加到鹅身上,似乎在添加其他属性时效果很好。

似乎不​​对劲的是,我仍然需要为每种鸟类创建一个自定义苍蝇,而且可能有很多鸟类!默认空隙对我有帮助吗?我觉得随着这些属性的增长,它们可能会受到相当的限制。

也许可以告诉我其他情况以及如何重新考虑这个接口结构。

选项 2

我自己的属性Class

这涉及创建属性 objects 并将它们添加到单个鸟 classes。如果我对接口的看法是正确的,那么这个选项可能是一种有效的方法。

属性 class 看起来像这样

public abstract class Attribute {
    public abstract void execute();
}

属性可能如下所示

public class Fly extends Attribute{
    @Override
    public void execute() {
        //fly!
    }
}

现在可以通过在 Bird 中拥有一个属性列表并将其填充到鸟类类型 classes.

来将此飞行属性添加到鸟类中
public abstract class Bird {
    private List<Attribute> attributes = new ArrayList<Attribute>();

    protected void addAttribute(Attribute attribute){
        attributes.add(attribute);
    }
    public List<Attribute> getAttributes(){
        return attributes;
    }
}

并将其添加到 Goose 中...

public class Goose extends Bird{
    public Goose(){
        super.addAttribute(new Fly());
    }
}

这种方法对我来说似乎非常可定制,但也似乎不合实际,可能不会立即清楚每个 class 能够做什么。它还需要大量工作才能在更大规模上正确设置才能执行特定属性。

有没有一种方法可以被认为是更好的做法,或者这些都是合理的方法还是一个好的开始?

如果所有飞鸟共享相同的飞行方法,您可以使用多级继承。

创建一个主要 Bird 对象来处理所有鸟类可以执行的操作。然后创建两个扩展 Bird 的子 class,比如 FlyingBirdGroundedBird。这样区分Bird.

每种能力比较合适
abstract class Bird {
    void eat() {
        // Because all birds get hungry
    }
}

abstract class FlyingBird extends Bird {
    void fly() {
        // Do the flight!
    }
}

abstract class GroundedBird extends Bird {
    void waddle() {
        // They gotta get around somehow.
    }
}

class Penguin extends GroundedBird;
class Goose extends FlyingBird;

编辑

还有几个其他选项可用于处理更多属性。 OP 还询问(在下面的评论中)如果鸟可以飞 游泳怎么办?

在继承链中的某个点,您将需要以任一方式实现行为。如果您要定义 Bird 的多个属性,则可以改用接口:

class Goose extends Bird implements Flyable {
    @Override
    public void fly() {
        // Need to override the fly() method for all flying birds.
    }
}

现在,假设您希望所有会飞的鸟都具有相同的飞行动作。虽然不一定是想法,但您可以创建一个名为 BirdAction 的静态 class 来容纳一只鸟可能具有的所有 "verbs"。

您仍然需要为每个 Bird 覆盖 fly() 方法,但让它们都在 BirdAction:

中调用相同的静态方法
class BirdAction {
    static void fly(Bird bird) {
        // Check if this Bird implements the Flyable interface
        if (bird instanceof Flyable) {
            // All birds fly like this
        } else {
            // This bird tried to fly and failed
        }
    }
}

我不会说这是一个理想的解决方案,但根据您的实际应用程序,它可能会奏效。 当然,默认情况下 Penguin 不会有 fly() 方法,但检查存在以防您仍然从 Penguin [=] 调用 BirdAction.fly() 51=].

这只是围绕面向对象设计的一些基本思考。如果没有更具体的信息,就很难就设计 classes 或接口做出明确的决定。

继承、行为和属性主要影响设计。设计方式还取决于大小(数量、类型和种类 - 想到)。可以看一下 Java 语言本身的继承设计,例如集合 - Collection 接口和 Map 接口及其实现。

以下是一些即时的想法:

public interface Bird {
    public void eat();
    public void layEggs();
}

// Provides implementations for _some_ abstract methods and _no_ new methods.
// Also, some implementations can just be empty methods.
public abstract class AbstractBird implements Bird {
    public void eat() {
        // generic eating implementation
    }
    public abstract void layEggs(); // no implementation at all
}


// Specific to ground-oriented with Bird behaviour
public interface GroundBird extends Bird {
    public void walk();
}


// Specific to air-oriented with Bird behavior
public interface FlyBird extends Bird {
    public void fly();
}


// Ground-oriented with _some_ bird implementation 
public class Penguin extends AbstractBird implements GroundBird {
    public void walk() {
    }
    public void layEggs() {
        // lays eggs on ground
    }
    // Can override eat if necessary
}


// Air-oriented with _all_ bird implementation 
public class Eagle implements FlyBird {
    public void fly() {
    }
    public void layEggs() {
        // lays eggs on trees
    }
    public void eat() {
        // can eat while flying
    }
}

此设计将允许:

  • 稍后在Bird接口中提供更多类似pluck()的方法,仅在AbstractBird.
  • 中实现
  • 还有一些类别 classes 可以完全跳过 AbstractBird 并且 直接用更具体的实现实现 Bird 接口 eatlayEggs。这也允许一种新的鸟类扩展 新的 class 或摘要 class.
  • 稍后添加 WaterBirds* 等其他鸟类。

*

public interface WaterBird {
    public void swim();
}
public class Duck implements WaterBird, GroundBird {
    public void swim() {
    }
    public void walk() {
    }
    public void eat() {
    }
    public void layEggs() {
        // lays eggs on ground
    }
}

并且可以创建新的界面,例如 Diveable 用于可以潜入水中捕鱼的鸟类和 Glideable 用于可以翱翔和滑翔的鸟类。可以看出 Glideable 是飞鸟的一种特殊行为。另请注意 Glidable 也是 HangGlider 的行为,并且由鸟和飞机共享。

public interface Glideable {
    public void glide();
}

public class Eagle extends AbstractBird implements FlyBird, Glideable {
    public void fly() {
    }
    public void glide() {
        // eagle specific
    }
    public void layEggs() {
        // lays eggs on trees
    }
    // Can override eat if necessary
}


注意:从 Java 8 开始,可以考虑在接口中使用 staticdefault 方法。