这可能是一个不好的例子,但请使用它。我有一个超类Cake
和两个子类:ExampleCake
和SaleCake
。我还有一个Baker
可以对蛋糕进行逆向工程以烘焙它的副本,然后知道如何处理它。
(所有伪代码)
public class Cake{
radius;
height;
ingredients;
color;
name;
}
public class ExampleCake extends Cake{
shelfLocation;
}
public class SaleCake extends Cake{
owner;
}
public class Baker{
Cake bakeCake(Cake);
Cake bakeSaleCake(Cake, Owner);
Cake bakeExampleCake(Cake, Location);
void handleCake(Cake);
}
Baker需要知道如何处理泛型Cakes,ExampleCakes和SaleCakes。所以他的bakeCake函数看起来像这样:
Cake bakeCake(cake){
newCake = cake.copy();
mixIngredients(newCake);
putCakeInOven(newCake);
putIcing(newCake);
return cake;
}
Cake bakeSaleCake(cake, owner){
newCake = bakeCake(cake);
newCake.setOwner(owner);
return newCake;
}
Cake bakeExampleCake(cake, location){
newCake = bakeCake(cake);
newCake.setLocation(location);
return newCake;
}
void handleCake(cake){
if(cake instanceof ExampleCake)
putOnShelf((ExampleCake)cake);
else if(cake instanceof SaleCake)
giveToCustomer((SaleCake)cake);
else
putOnTable(cake);
}
我的问题是贝克硬编码只处理某些类型的蛋糕。如果出现新型蛋糕,他就无法处理。是否有一种干净,通用的方法来处理它,或者是我上面的伪代码"足够好" (因为,它不会伤害你的眼睛或心脏)?
感谢。
答案 0 :(得分:1)
要复制蛋糕,您可以使用复制构造函数。这种方式取决于特定的蛋糕如何复制自己。要处理任意蛋糕,您可以使用访客模式。通过访问者,您可以“询问”有关其类型的蛋糕,而不是使用instanceof
检查类型。
代码看起来像这样(除了copy-constructors之外,你还希望有其他的Cake构造函数):
// Cake class
public class Cake{
int radius;
int height;
public Cake(Cake cake){
this.radius = cake.radius;
this.height = cake.height;
}
<R> R accept(CakeVisitor<? extends R> visitor){
return visitor.visit(this);
}
}
// ExampleCake类
public class ExampleCake extends Cake{
String shelfLocation;
public ExampleCake(Cake cake, String shelfLocation){
super(cake);
this.shelfLocation = shelfLocation;
}
<R> R accept(CakeVisitor<? extends R> visitor){
return visitor.visit(this);
}
}
// SaleCake类
public class SaleCake extends Cake{
String owner;
public SaleCake(Cake cake, String owner){
super(cake);
this.owner = owner;
}
<R> R accept(CakeVisitor<? extends R> visitor){
return visitor.visit(this);
}
}
//访客界面
public interface CakeVisitor<R> {
R visit(Cake cake);
R visit(SaleCake cake);
R visit(ExampleCake cake);
}
// Baker类
public class Baker {
private final CakeVisitor<Void> cakeHandler = new CakeVisitor<Void>(){
@Override
public Void visit(Cake cake) {
putOnTable(cake);
return null;
}
@Override
public Void visit(SaleCake cake) {
giveToCustomer(cake);
return null;
}
@Override
public Void visit(ExampleCake cake) {
putOnShelf(cake);
return null;
}
};
Cake bakeCake(Cake cake){
return processedCake(new Cake(cake));
}
SaleCake bakeSaleCake(Cake cake, String owner){
return processedCake(new SaleCake(cake, owner));
}
ExampleCake bakeExampleCake(Cake cake, String location){
return processedCake(new ExampleCake(cake, location));
}
void handleCake(Cake cake){
cake.accept(cakeHandler);
}
private <C extends Cake> C processedCake(C cake){
mixIngredients(cake);
putCakeInOven(cake);
putIcing(cake);
return cake;
}
}
现在,如果您想要使用Baker处理新类型的蛋糕,默认情况下它将被放在桌面上,除非您提供一个访问方法,该方法专门接受新蛋糕类型的对象。我认为你不能真正使它更通用,因为当你有一种新类型的蛋糕时,你想要以不同于其他类型蛋糕的方式处理它,处理它的新代码必须去某个地方。像putOnTable()
这样的代码不应放在蛋糕类中,因为蛋糕不知道它是如何放在桌子上的 - 面包师知道 - &gt;因此访客模式。
答案 1 :(得分:0)
良好的设计是关于识别哪些行为发生变化,哪些行为保持不变,然后将这两个组分开。这里改变的是贝克对每个蛋糕的行为。所以我们想要创建一个可以封装他不断变化的行为的新类。这是我们的做法。使用名为doBehavior()的单个方法创建一个名为BakerBehavior的接口。然后,创建一个名为PutOnShelfBehavior的类,另一个名为GiveToCustomerBehavior,它们都实现了BakerBehavior。覆盖doBehavior()方法。
现在,在每个蛋糕类中,您可以创建PutOnShelfBehavior对象或GiveToCustomerBehavior对象。你的蛋糕超类将有一个act()方法,如下所示:
//Constructor {
myBehaviorObject=new PutOnShelfBehavior();
}
public void act() {
myBehaviorObject.doBehavior();
}
现在,在您的Baker类中,这就是您的handleCake方法的样子:
private void handleCake(Cake cake) {
cake.act();
}
我没有看到任何其他方式这样做。这样做的好处是:A)不必在handleCake类中硬编码蛋糕类型,B)你不必重复任何代码(例如,你可能有2种不同的蛋糕类型放在架子上)。 C)每个蛋糕都不了解贝克的任何信息,它只知道它会被放在架子上或其他任何东西上。我希望有人能告诉我是否有更好的方法,我对这个问题很感兴趣。