2009-10-16 61 views
9

複合材料中的葉子實現了具有他將永遠不會使用的添加移除和GetChild方法的組件(違反界面隔離)複合模式是否爲SOLID?

所以使用複合模式SOLID

鏈接到複合http://www.dofactory.com/Patterns/PatternComposite.aspx

+0

你的問題還不清楚。另外,你是什麼意思的「固體」 – monksy 2009-10-16 18:35:45

+0

我爲上面的提問者添加了一個SOLID鏈接。 – SingleShot 2009-10-16 18:49:11

+0

SRP和LSP也與ISP一起受到侵犯。所以SOLID是危險的。 :) – Narek 2016-09-22 08:18:01

回答

10

在爲你的鏈接和大多數書籍中描述的模式真正的氣味是ComponentComposite的方法。我想這可能是因爲這種模式相當古老,並且多年來一直這樣重複。我的意見是,只有Composite應該有任何與合成相關的方法。

我曾經將棋盤遊戲轉換爲電腦遊戲。把棋子放在地球地圖上,分成六邊形。所有六邊形中99%代表一個位置。不幸的是,一些六邊形包含多個位置,例如,一些內部有一些島。我使用複合模式來表示這些位置,但不是如鏈接所示。它是這樣的(在Java中):

public interface Location { 
    Set<Army> getArmies(); 
} 

public class SingleLocation implements Location { 

    public Set<Army> getArmies() { 
     return armies ; 
    } 

    private Set<Army> armies = new HashSet<Army>(); 
} 

public class CompositeLocation implements Location { 

    public Set<Army> getArmies() { 

     Set<Army> armies = new HashSet<Army>(); 

     for(Location subLocation: subLocations) { 
     armies.addAll(subLocation.getArmies()); 
     } 

     return armies; 
    } 

    public void addSubLocation(Location location) { 
     subLocations.add(location); 
    } 

    private Set<Location> subLocations = new HashSet<Location>(); 
} 

請注意,只有Composite具有合成方法,甚至不公開,它有孩子大部分客戶(在這個例子中,實際上,客戶端只希望一個地點的軍隊名單 - 事實上,他們在許多分地點是無關緊要的)。

請記住,設計模式不是一成不變的東西,您必須準確實施。把它們想象成食譜。當你在烹飪時遵循食譜,你當然可以完全遵循它。但是,有些廚師會根據自己的食譜來制定自己的做法。其他人甚至都不會去看它,因爲他們是專家,可以根據食譜的精神把東西放在一起,甚至不用考慮。設計模式也是如此。他們是可延展的食譜。

你也可以採取那些固體原則太過分。如果你閱讀羅伯特馬丁的文章,他表示,毫不猶豫地應用這些原則會產生過於複雜的代碼。軟件的設計是通過一系列的權衡和平衡 - 有時候你會放棄純粹的SOLID,因爲它會產生更簡潔更簡單的代碼。如果你想使你的代碼完全封裝,靈活,解耦等,你將發明一種新的編程語言:-)

+0

這是非常普遍的,有很好的理由 - 但有標準版本的原因。例如,除了簡單的合成之外,這些項目可能具有相互之間的函數依賴關係 - 例如,段落的高度取決於每行文本的高度,這取決於行內每個字符的高度。使用單獨的散列表示合成的項目必須返回複合材料才能找到其直系親屬。當然,這也具有優勢 - 組合可以對該段落高度等子樹摘要負責。 – Steve314 2009-10-16 20:46:19

5

我會說,你的鏈接中描述的複合模式違反Liskov substitution principle,其中之一五個原則。

Component有一些方法僅對於例如Composite有意義, Add()LeafComponent繼承,所以它將有一個Add()方法像其他Component一樣。但是Leafs沒有孩子,所以下面的方法調用不能返回有意義的結果:

myLeaf.Add(someChild);

這個調用將不得不拋出一個MethodNotSupportedException,在一些其他的方式返回null或指示,加入主叫一個Leaf的孩子沒有意義。

因此,您不能像其他Component那樣對待Leaf,因爲如果您嘗試使用,您會得到異常。所述里氏substition原理的狀態:

設q(x)爲可證明約類型T.則Q(Y)的 對象X的屬性應該 爲S型的對象ýtrue其中 S是一個子類型的T.

Components有財產,您可以添加孩子給他們。但是,即使LeafComponent的子類型,但您無法將子女添加到Leaf,這違反了原則。

+0

一個類型必須偏離與其父類型的語義互操作性,纔有資格成爲Liskov替換原則違反,所以如果Leaf類型這樣做,那麼它將是由於無法對孩子調用Add()並隨後致電帶預期索引的GetChild()。但是,僅僅省略行爲不會成爲違規行爲。 Add()方法沒有隱式或顯式的合約,要求它提供添加子項的反饋,但有一個隱式合約,假設可以通過GetChild()方法檢索添加的子項。 – 2009-10-20 05:01:39

+0

順便說一句,空對象模式有效的原​​因是因爲它在保持語義互操作性的同時忽略了行爲。 – 2009-10-20 05:07:41

0

使用Composite可以讓你一致地處理所有的對象,並可能擺脫「instanceOf」這個代碼異味的明顯標誌。所以它乍一看就是對LSP(Liskov's)的尊重。然而,當你區分常見的方法實現時,它很可能開始違反LSP。所以,國際海事組織,我們需要保持這種平衡。