繁体   English   中英

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

[英]Attribute Inheritance with Interfaces and Abstract Classes - Java

好吧,所以我试图为此找到最好的结构。 我想知道在这些情况下以及整体上最有效的最佳实践。

问题

假设我正在创建一个小世界。 这个世界由不同类型的鸟类组成,因此我创建了一个Bird类来充当所有鸟类的父代。

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

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

public class Penguin extends Bird{
    //penguin stuff
}

public class Goose extends Bird{
    //goose stuff
}

到目前为止一切顺利,但现在是扳手。 让我们赋予鸟类飞行的能力。 令人惊讶的是 ,并非所有的鸟类都能飞翔-例如企鹅! 因此,让我们来探讨一下我们的选择。

在Bird类内部创建fly方法是不可行的(并非所有鸟都可以飞行)。 另一种令人讨厌的方式是拥有多个父类,例如BirdsThatFly,因为这会变得一团糟,尤其是在将其他属性添加到混合中时。

选项1

接口

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

public interface Fly {
    public void fly();
}

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

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

这看起来很干净,并强制将fly方法应用到鹅上,并且似乎在添加其他属性时确实可以很好地工作。

似乎不对的地方是,我仍然必须为每种类型的鸟创建自定义苍蝇,并且可能会有很多鸟! 默认空缺会在这里帮助我吗? 我认为随着这些属性的增长,它们可能会受到相当大的限制。

也许可以告诉我其他方式以及如何重新考虑此接口结构。

选项2

我自己的属性类

这涉及创建属性对象并将其添加到单个鸟类。 如果我对接口正确,则此选项可能是有效的方法。

属性类看起来像这样

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

一个属性可能看起来像这样

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

现在,可以通过在Bird中具有一个属性列表并将其填充在bird类型类中,将该fly属性添加到鸟。

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;
    }
}

并将其添加到鹅...

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

对我来说,这种方法似乎非常可定制,但似乎不可行,可能无法立即弄清每个班级的能力。 为了能够执行特定的属性,还需要进行大量工作才能正确地大规模设置。

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

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

创建一个主Bird对象,该对象处理所有Bird可以执行的操作。 然后创建两个扩展Bird子类,例如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多个属性,则可以使用Interfaces代替:

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

现在,假设您希望所有飞鸟都具有相同的飞行动作。 尽管不一定是主意,但您可以创建一个称为BirdAction的静态类来容纳鸟可能具备的所有“动词”。

您仍然需要为每个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() ,则可以进行检查。

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

继承,行为和属性主要影响设计。 设计方式还取决于大小(想到的数量,类型和种类)。 可以看看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实现。
  • 一些类别类也可以完全跳过AbstractBird并直接使用eatlayEggs更具体的实现来实现Bird接口。 这也使鸟类的新类别扩展了新类别或抽象类别。
  • 在以后添加其他种类的鸟类,例如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 (可滑翔而能够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开始,可以考虑在接口中使用静态默认方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM