Variable resets to default value when I decorate object

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.


Your implementation of the Decorator Pattern is not correct conceptually. The problem you're encountering comes from that incorrect implementation.

I'm referring below to the 5 items in this wikipedia article: https://en.wikipedia.org/wiki/Decorator_pattern

  • "Subclass the original "Component" class into a "Decorator" class;"
  • in your case the "Component" is Beverage and CondimentDecorator is the Decorator
  • "In the Decorator class, add a Component pointer as a field;"
  • Add the Component pointer as a field. You added the field Beverage beverage in Soy , instead of adding it to CondimentDecorator
  • "Pass a Component to the Decorator constructor to initialize the Component pointer;"
  • This should be done in CondimentDecorator as well
  • "In the Decorator class, redirect all "Component" methods to the "Component" pointer"
  • You haven't done that, and it is the root of your issue. On a CondimentDecorator , by default, getSize() should call beverage.getSize() . The same should be done for all other fields.
  • I think you should go back to the basics of what the decorator pattern is trying to achieve, and rebuild your implementation with the right idea in mind. You might be able to fix your issue in a simpler way, but you might then have a wrong idea of what the decorator pattern actually is.

    Keep in mind the principal idea that:

  • Beverage is your Component,
  • CondimentDecorator is your Decorator and,
  • Soy is a ConcreteDecorator

  • i think you have to delegate getSize() to the wrapped/decorated instance if you want the value of the "original" beverage.

      Beverage cappuccino = new Cappuccino(Size.VENTI);
      Beverage soyMilkCappuccino = new Soy(cappuccino);
    

    Inside Soy

      class Soy extends Beverage {
           private final Beverage decorated;
           Soy(Beverage other) { this.decorated = other; }
           // delegation
           Size getSize() { return decorated.getSize(); }
      }
    
    链接地址: http://www.djcxy.com/p/32008.html

    上一篇: 抽象装饰器类中的功能而不是装饰器

    下一篇: 当我装饰物体时,变量重置为默认值