2013-11-26 76 views
2

更好的方法是首先選擇合成,特別是當它不是你明顯應該使用哪一個時。構圖不會強制設計成爲繼承層次結構。但是組合也更靈活,因爲在使用組合時可以動態地選擇一個類型(因而動態地選擇 行爲),而在編譯時繼承需要知道確切類型 。下面的例子 說明了這爲什麼組成可以動態選擇一種類型

import static net.mindview.util.print.*; 
class Actor { 
    public void act() {} 
} 

class HappyActor extends Actor { 
    public void act() {print("HappyActor"); } 
} 

class SadActor extends Actor { 
    public void act() {print("SadActor"); } 
} 

class Stage { 
    private Actor actor = new HappyActor(); 
    public void change() { actor = new SadActor(); } 
    public void performPlay() {actor.act(); } 
} 

public class Transmogrify { 
    public static void main(String[] args) { 
    Stage stage = new Stage(); 
    stage.performPlay(); 
    stage.change(); 
    stage.performPlay(); 
    } 
} 

/*輸出: HappyActor SadActor * ///:〜

我讀Java中的書的思想,我我以前不理解句子如何組合動態選擇類型?可以有人向我解釋,謝謝

回答

1

隨着組合,實際對象的(動態)類型不是成員的(靜態)類型。它可以是該類型的任何子類。

E.g.在你的例子中,成員actor可以指向Actor的繼承樹中的任何對象。所以actor也可以指向HappyActorSadActor。因此,您可以在運行時擁有不同的動態類型。

具有繼承,實際類型將被固定:

class Actor { 
    public void act() {} 
} 

class HappyActor extends Actor { 
    public void act() {print("HappyActor"); } 
} 

class Stage extends HappyActor { 
    public void change() { /* Cannot change dynamic type */ } 
    public void performPlay() {this.act(); } // call inherited act method 
} 

這將始終輸出HappyActor

+0

然後使用合成,你可以,然後,例如,基於配置文件或其他發生在系統中(或玩,堅持域語言),改變哪種類型的演員正在執行 –

+1

怎麼可能'階段'永遠延長'HappyActor'? –

+0

問題在於組合如何提供動態類型,而不是繼承。這只是一個例子,其他方式如何,使用提供的類 –

1

這也許措辭不當。這不是關於類型,而是行爲。當你偏愛合成而不是繼承時,行爲可以在運行時動態地改變 - 當你將它與不限制你的類型層次結構的接口相匹配時,它的效果會更好。

考慮標準:

interface Painter { 
    public String getName(); 
    public void paint(Canvas c); 
} 

class Canvas { 
    @Override 
    public String toString() { 
     return "a canvas."; 
    } 
} 

class NouveauPainter implements Painter { 
    public void paint(Canvas c) { 
     System.out.printf "%s Painted a nouveau stroke on %s.\n", 
      getName(), 
      c); 
    } 
public String getName() { 
    return getClass().getSimpleName(); 
} 

} 

class ClassicPainter implements Painter { 
    public void paint(Painter p, Canvas c) { 
     System.out.printf "%s Painted a classic stroke on %s.\n", 
      getName(), 
      c); 
    } 
public String getName() { 
    return getClass().getSimpleName(); 
} 

} 

public class DoSomePainting() { 
    public static void main(String...args) { 
     Canvas c = new Canvas(); 
     Painter oldschool = new ClassicPainter(); 
     oldschool.paint(c); 

     Painter newschool = new NouveauPainter(); 
     oldschool.paint(c); 
    } 
} 

你必須選擇一個類型的畫家或其他的。但是如果你想在運行時改變筆畫呢?這通常是必要的。如果將paint方法嵌入到父類(或抽象類)中,那麼如果要重用功能並可能最終產生各種抽象抽象和神類,則會被卡在類型層次結構中。考慮follwing:

interface Stroke { 
    public void paint(Painter p, Canvas c) 
} 

class ClassicStroke { 
    public void paint(Painter p, Canvas c) { 
     System.out.printf "%s Painted a classic stroke on %s.\n", 
      p.getName(), 
      c); 
    } 
} 

class NouveauStroke { 
    public void paint() { 
     System.out.printf "%s Painted a nouveau stroke on %s.\n", 
      p.getName(), 
      c); 
    } 
} 


class GenericPainter implements Painter { 

    public Stroke stroke = null; 

    public void paint(Canvas c) { 
     stroke.paint(this, c); 
    } 

public String getName() { 
    return getClass().getSimpleName(); 
} 


} 

public class DoSomePainting() { 
    public static void main(String...args) { 
     Canvas c = new Canvas(); 
     Painter versatile = new GenericPainter(); 

     versatile.stroke = new ClassicStroke(); 
     versatile.paint(c); 
     versatile.stroke = new NouveauStroke(); 
     versatile.paint(c); 

    } 
} 

現在畫家的定義,它的執行部件,被明確區分,可以重新用於任何地方。

注意:我不確定這是否全部編譯 - 這是更多的說明目的和文本框中的編碼是尷尬的。

+0

感謝您的回答,一個接口實際上效果更好,它不需要類型層次 – jianwei

+0

-shrug-組合更多地是在接口之後發生的事情。通常有一些默認的實現細節最終會出現在某種共同的祖先類中,而當事情開始走向奇怪的方向時。 大多數情況下,這個圖案都是兩個或者多個。一個輔助類/支持類,它實現了默認的功能,並採用內部接口將其與所組成的類,內部接口以及外部接口綁定。 我的意思是,這些想法是免費的,沒有衝突。 – lscoughlin

相關問題