简体   繁体   English

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

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

Alright, so I'm trying to figure out the best structure for this. 好吧,所以我试图为此找到最好的结构。 I would like to know what would be considered the best practice for these situations and the overall most effective. 我想知道在这些情况下以及整体上最有效的最佳实践。

The Problem 问题

Let's say I'm creating a little world. 假设我正在创建一个小世界。 This world consists of different types of birds, So I create a Bird class to act as a parent for all birds. 这个世界由不同类型的鸟类组成,因此我创建了一个Bird类来充当所有鸟类的父代。

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

Now I want to create different types of birds. 现在,我要创建不同类型的鸟类。 Let's create a Penguin and a Goose class and have them extend Bird. 让我们创建一个Penguin和Goose类,并让它们扩展Bird。

public class Penguin extends Bird{
    //penguin stuff
}

public class Goose extends Bird{
    //goose stuff
}

So far so good, but now for the wrench. 到目前为止一切顺利,但现在是扳手。 Let's give birds the ability to fly. 让我们赋予鸟类飞行的能力。 Surprise , not all birds can fly - such as the penguins! 令人惊讶的是 ,并非所有的鸟类都能飞翔-例如企鹅! So let's approach our options.. 因此,让我们来探讨一下我们的选择。

Making a fly method inside of the Bird class is out of the option (not all birds fly). 在Bird类内部创建fly方法是不可行的(并非所有鸟都可以飞行)。 Another smelly way is having multiple parent classes, such as BirdsThatFly, because this would become an enormous mess, especially when other birdly attributes are added into the mix. 另一种令人讨厌的方式是拥有多个父类,例如BirdsThatFly,因为这会变得一团糟,尤其是在将其他属性添加到混合中时。

Option 1 选项1

Interfaces 接口

One viable option that in theory sounds like the right idea would be to use interfaces. 理论上听起来像是正确想法的一个可行选择是使用接口。 For example, I could have a Fly interface. 例如,我可以有一个Fly界面。

public interface Fly {
    public void fly();
}

This would allow me to implement Fly on only Bird child classes that can fly. 这将允许我仅在可以飞行的Bird子类上实现Fly。 Example.. 例..

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

This appears clean and forces a fly method onto the goose, and seems it could work really well when adding other attributes. 这看起来很干净,并强制将fly方法应用到鹅上,并且似乎在添加其他属性时确实可以很好地工作。

The thing that seems wrong, is that I would still have to create a custom fly for each type of bird, and there could be loads of birds! 似乎不对的地方是,我仍然必须为每种类型的鸟创建自定义苍蝇,并且可能会有很多鸟! Would default voids help me here? 默认空缺会在这里帮助我吗? I feel they could be fairly limiting as these attributes grow. 我认为随着这些属性的增长,它们可能会受到相当大的限制。

Perhaps I can be told otherwise and how to rethink this interface structure. 也许可以告诉我其他方式以及如何重新考虑此接口结构。

Option 2 选项2

My Own Attribute Class 我自己的属性类

This involves creating attribute objects and adding them to the individual bird classes. 这涉及创建属性对象并将其添加到单个鸟类。 If I'm correct with interfaces, this option may be a valid approach. 如果我对接口正确,则此选项可能是有效的方法。

The attribute class would look something like this 属性类看起来像这样

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

And an attribute could look like this 一个属性可能看起来像这样

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

This fly attribute can now be added to a bird by having an attribute list in Bird, and populating it in the bird type classes. 现在,可以通过在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;
    }
}

And adding it to a Goose... 并将其添加到鹅...

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

This approach to me seems very customizable, but also seems out of practice and maybe not immediately be clear what each class is able to do. 对我来说,这种方法似乎非常可定制,但似乎不可行,可能无法立即弄清每个班级的能力。 It also requires a lot of work to properly set up on larger scale to be able to execute specific attributes. 为了能够执行特定的属性,还需要进行大量工作才能正确地大规模设置。

Is there a way that would be considered better practice, or are either of these a reasonable approach or a good start? 有没有一种方法可以被认为是更好的做法,或者这是一个合理的方法还是一个好的开始?

If all flying birds share the same method of flying, you could use multiple levels of inheritance. 如果所有飞鸟共享相同的飞行方法,则可以使用多个继承级别。

Create a main Bird object that handles the actions ALL birds can do. 创建一个主Bird对象,该对象处理所有Bird可以执行的操作。 Then create two subclasses that extend Bird , say FlyingBird and GroundedBird . 然后创建两个扩展Bird子类,例如FlyingBirdGroundedBird This is a more appropriate way to separate the abilities of each type of Bird . 这是分离每种类型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;

EDIT 编辑

There are a couple of other options for handling more attributes. 还有其他两个选项可用于处理更多属性。 The OP also asks (in comments below) what to do if the bird can fly and swim? OP还询问(在下面的评论中)如果鸟可以飞翔游泳怎么办?

At some point in the inheritance chain, you will need to implement the behavior either way. 在继承链的某个时刻,您将需要以任何一种方式实现行为。 If you are looking to define multiple attributes of a Bird , this would be a case to use Interfaces instead: 如果要定义Bird多个属性,则可以使用Interfaces代替:

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

Now, the suppose you want all flying birds to have the same action for flying. 现在,假设您希望所有飞鸟都具有相同的飞行动作。 While not necessarily idea, you could create a static class called BirdAction to hold all the "verbs" a bird might be capable of. 尽管不一定是主意,但您可以创建一个称为BirdAction的静态类来容纳鸟可能具备的所有“动词”。

You would still need to override the fly() method for each Bird but have them all call the same static method within 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
        }
    }
}

I would not say that's an ideal solution, but depending on your real-world application, it may just work. 我不会说这是理想的解决方案,但是取决于您的实际应用程序,它可能会起作用。 Granted, a Penguin would not have the fly() method by default, but the check is there in case you still call BirdAction.fly() from the Penguin class. 当然,默认情况下, Penguin不会具有fly()方法,但是如果您仍然从Penguin类中调用BirdAction.fly() ,则可以进行检查。

This is just some basic thinking around object-oriented design. 这只是围绕面向对象设计的一些基本思路。 Without more specific information its difficult to make clear decisions about one goes about designing classes or interfaces. 没有更具体的信息,就很难做出明确的决定,那就是设计类或接口。

Inheritance, behavior and attributes mostly influence the design. 继承,行为和属性主要影响设计。 The way to design also depends upon the size (number of, type of and kind of - come to mind). 设计方式还取决于大小(想到的数量,类型和种类)。 One can take a look at the inheritance design in Java language itself, eg, collections - both the Collection interface and the Map interface and their implementations. 可以看看Java语言本身的继承设计,例如集合- Collection接口和Map接口及其实现。

Here are some instant thoughts: 以下是一些即时想法:

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

This design will allow: 此设计将允许:

  • provide more methods like pluck() in the Bird interface later on and has implementation only in AbstractBird . 稍后在Bird界面中提供更多方法,例如pluck() ,并且仅在AbstractBird实现。
  • also some category classes can skip the AbstractBird altogether and directly implement Bird interface with more specific implementations of eat and layEggs . 一些类别类也可以完全跳过AbstractBird并直接使用eatlayEggs更具体的实现来实现Bird接口。 This also allows a new category of bird extend a new class or abstract class. 这也使鸟类的新类别扩展了新类别或抽象类别。
  • to add other kind of birds like WaterBirds * at a later date. 在以后添加其他种类的鸟类,例如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
    }
}

And can create new interfaces like Diveable for birds that can dive into water to catch fish and Glideable for birds that can soar and glide. 并可以创建新的界面,例如“可潜水”( Diveable可以潜入水中Glideable的鸟类,以及“ Glideable (可滑翔而能够Glideable的鸟类)。 One can see Glideable is a special behaviour for flying birds. 可以看到“ Glideable是飞鸟的一种特殊行为。 Also note the Glidable is also a behavior of HangGlider and is shared by both the bird and an aircraft. 还要注意, 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
}


NOTE: As of Java 8 one can consider using static and default methods in interfaces. 注意:从Java 8开始,可以考虑在接口中使用静态默认方法。

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

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