如何使实用程序类处理子类中的差异?

时间:2017-01-25 23:58:25

标签: java inheritance subclass

这可能是一个不好的例子,但请使用它。我有一个超类Cake和两个子类:ExampleCakeSaleCake。我还有一个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);
}

我的问题是贝克硬编码只处理某些类型的蛋糕。如果出现新型蛋糕,他就无法处理。是否有一种干净,通用的方法来处理它,或者是我上面的伪代码"足够好" (因为,它不会伤害你的眼睛或心脏)?

感谢。

2 个答案:

答案 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)每个蛋糕都不了解贝克的任何信息,它只知道它会被放在架子上或其他任何东西上。我希望有人能告诉我是否有更好的方法,我对这个问题很感兴趣。