更好的方法是首先选择构图,尤其是在构图时 不明显你应该使用哪一个。作文不强迫 设计成继承层次结构。但成分也更多 灵活,因为它可以动态选择一种类型(因此 使用组合时,继承需要一个 在编译时要知道的确切类型。以下示例 说明了这个
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思考的书,但我并没有理解句子如何动态选择一种类型?任何人都可以向我解释,谢谢
答案 0 :(得分:1)
使用合成,实际对象的(动态)类型不是成员的(静态)类型。它可以是该类型的任何子类。
E.g。在您的示例中,成员actor
可以指向Actor
的继承树中的任何对象。因此,actor
也可以指向HappyActor
或SadActor
。因此,您可以在运行时使用不同的动态类型。
使用继承,实际类型将被修复:
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);
}
}
现在,画家的定义及其部分的实现是明确分开的,可以在任何地方重复使用。
注意:我不确定这是否全部编译 - 它更多用于说明目的,在文本框中编码很尴尬。