I'm trying to learn the Decorator Design Pattern. In this example, I create an app that calculates the cost of a coffee based on its type (espresso, decaf, house blend), size (tall, grande, venti), and condiment-decorators (soy, whipped cream, steamed milk) -- some code excluded for brevity.
As you can see from the output at the bottom, if I setSize(GRANDE) and don't wrap the object, getSize() returns GRANDE.
If I setSize(GRANDE), then decorate the object, getSize() returns TALL (the default value set in the Beverage class).
If I setSize(GRANDE), decorate the object, setSize(GRANDE) again, then getSize() returns GRANDE.
Note: Though the sizes don't print correctly, the costs are calculated correctly.
QUESTION: Is there a way to code this so when I setSize() it keeps that value even after the object is decorated?
package coffee;
public abstract class Beverage {
String description = "Unknown Beverage";
public enum Size { TALL, GRANDE, VENTI };
Size size = Size.TALL;
public String getDescription() {
return description;
}
public void setSize(Size size) {
this.size = size;
}
public Size getSize() {
return size;
}
public abstract double cost();
}
package coffee;
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
package coffee;
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
public double cost(){
return .89;
}
}
package coffee;
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
public double cost() {
double cost = beverage.cost();
if( beverage.getSize() == Size.TALL) {
cost += .10;
} else if( beverage.getSize() == Size.GRANDE) {
cost += .15;
}else if( beverage.getSize() == Size.VENTI) {
cost += .20;
}
return cost;
}
}
package coffee;
import coffee.Beverage.Size;
public class StarbuzzCoffeeController {
public static void main(String[] args) {
Beverage beverage = new HouseBlend();
System.out.println( beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost() ));
beverage.setSize(Size.GRANDE);
System.out.println( beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost() ));
System.out.println("-----------------------");
Beverage beverage2 = new HouseBlend();
beverage2.setSize(Size.GRANDE);
System.out.println( beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost() ));
If I don't follow this with another setSize() it prints "TALL" as the size on the next line:
beverage2 = new Soy(beverage2);
System.out.println( beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost() ));
System.out.println("-----------------------");
Beverage beverage3 = new HouseBlend();
beverage3.setSize(Size.GRANDE);
System.out.println( beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost() ));
beverage3 = new Soy(beverage3);
If I setSize() again after I've decorated the object, the size prints correctly as GRANDE:
beverage3.setSize(Size.GRANDE);
System.out.println( beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost() ));
}
}
OUTPUT::
TALL House Blend Coffee $0.89
GRANDE House Blend Coffee $0.89
-----------------------
GRANDE House Blend Coffee $0.89
TALL House Blend Coffee, Soy $1.04
-----------------------
GRANDE House Blend Coffee $0.89
GRANDE House Blend Coffee, Soy $1.04
Your class Soy does not override the getSize() method - therefore, the default implementation of the base class is used. And the base class returns its own size member, which is initialized to TALL.
In order to fix this problem, you will have to override the getSize() method accordingly in your class Soy:
@Override
public Size getSize() {
return this.beverage.getSize();
}
This will correctly return the size of the decorated object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With