为什么组合可以动态选择一种类型

时间:2013-11-26 12:21:50

标签: java polymorphism

  

更好的方法是首先选择构图,尤其是在构图时   不明显你应该使用哪一个。作文不强迫   设计成继承层次结构。但成分也更多   灵活,因为它可以动态选择一种类型(因此   使用组合时,继承需要一个   在编译时要知道的确切类型。以下示例   说明了这个

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思考的书,但我并没有理解句子如何动态选择一种类型?任何人都可以向我解释,谢谢

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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方法嵌入在父类(或抽象类)中,那么如果你想重新使用功能,你可能会遇到类型层次结构,最终可能会遇到各种漏洞抽象和上帝类。考虑以下内容:

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);

    }
}

现在,画家的定义及其部分的实现是明确分开的,可以在任何地方重复使用。

注意:我不确定这是否全部编译 - 它更多用于说明目的,在文本框中编码很尴尬。