2012-09-21 59 views
0

昨天我問了this的問題,而@JB Nizet發佈的解決方案完美運作。然而,這個答案以及其他一些答案/評論讓我思考了一個完全不同的方向。面向對象:如何編寫類的家庭一起工作?

從本質上講,我有以下類別:

  • Load
  • HttpLoad extends Load
  • Target
  • HttpTarget extends Target
  • Controller

Controller的工作是Target::fire()一個Load,並且不關心哪個Target被解僱這Load

// Inside Controller.java 
Target target = getTarget(); 
Load load = getLoad(); 

target.fire(load); 

不過,我可能有一天寫一FtpLoad extends Load,我不希望能夠火一個FtpLoadHttpTarget。所以上面提到的問題的本質是我該如何做到這一點,其中,答案是泛型

但是,正如回答者指出的那樣,該解決方案違反了Liksov替代原則。其他回答者/評論者似乎表明我所做的並不一定是好的面向對象的做法。

所以我現在要問:我怎麼暴露的API,以便Controller可以Load - 和Target -agnostic,但仍執行了正確的Load子類是在正確的Target型發射,全部在不違反Liskov換人?

而且,如果這是不可能的(不違反Liskov),那麼對於這樣的問題的正常方法是什麼?提前致謝!

+1

我稱之爲模板繼承。使用繼承是因爲事物具有很多通用的功能/屬性,但實際上並沒有任何多態關係。 「正常」的方法是無論如何都要做,並且擁有充滿「支持」方法或類似的笨重API。另一種方法是封裝和多次調度,但是人們發現很難理解。 – Affe

+0

經驗法則:重寫的方法幾乎總是違反了Liskov可替代性,因爲它們在用於替代超類型時會改變子類型的行爲。 –

+0

更改行爲不違反原則。縮小前置條件或擴大後置條件。當將繼承用於框架模板化任務而不是建模多態關係時,通常會發生這種情況。 – Affe

回答

0

簡單的方法是在代碼中進行一些檢查以確保類匹配。您可以使用instanceof關鍵字來檢查它是否是正確的類。

1

如果HttpTarget.fire允許任何Load作爲參數,它的工作是檢查它是否可以fireLoad。所以,無論是Controller電話fire盲目和fire檢查,如果給定的目標能火的那種Load(與instanceof),或者您在每一個target實現這樣的檢查,由Controller調用的函數canFire

0

最好是實現抽象類或接口並使用前面提到的instanceof。

有了一個抽象類:

public abstract class TargetLoad { 
    public abstract void fire(TargetLoad i); 
} 

public class Load extends TargetLoad { 
    @Override 
    public void fire(TargetLoad i) { 
     if (i instanceof Target) return; 
     // do fire stuff 
    } 
} 

public class Target extends TargetLoad { 
    @Override 
    public void fire(TargetLoad i) { 
     if (i instanceof Load) return; 
     // do fire stuff 
    } 
} 

與接口:

public interface TargetLoad { 
    public void fire(TargetLoad i); 
} 

public class Load implements TargetLoad { 
    @Override 
    public void fire(TargetLoad i) { 
     if (i instanceof Target) return; 
     // do fire stuff 
    } 
} 

public class Target implements TargetLoad { 
    @Override 
    public void fire(TargetLoad i) { 
     if (i instanceof Load) return; 
     // do fire stuff 
    } 
} 

在你的控制器,你在這裏是指你的對象作爲TargetLoad

TargetLoad target = getTarget(); 
TargetLoad load = getLoad(); 

target.fire(load); 
load.fire(target); 
load.fire(load);  //this will do nothing 
target.fire(target); //this will do nothing 
1

打字的問題是, HttpTarget不是Liskov亞型Target,因爲語義上是它試圖強化Target#fire(Load)的前提條件,要求LoadHttpLoad

這可以通過聲明Target#fire(Load) throws IncompatibleLoadException並具有默認實現總是拋出,迫使Controller處理的事實不匹配Load可以被傳遞而輕易地修復。

0

我強烈的答案,建議擺不同意使用instanceof。編寫良好的OOP代碼很少需要使用instanceof,而使用instanceof通常會使代碼難以維護。作爲一般規則,儘可能避免instanceof

您提到的上一個問題提供了一種使用泛型的解決方案。我不確定是否在此處遺漏了您的問題的泛型代碼;回到你的通用代碼。現在,將以下方法添加到您的驅動程序。

private <L extends Load> void runSuite(TestSuite<L> suite) { 
    Target<L> target = testSuite.getTarget(); 
    L load = testSuite.getLoad(); 
    target.fire(load); 
}