簡體   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