Search code examples
javadecorator

Can I create a decorated object based on a list?


I have a program that can print a pizza with decorators. I have an interface:

public interface PizzaPie{
    String top();
}

And an implementation of the interface

public class PizzaPieImplementation implements PizzaPie{

    @Override
    public String top() {
        return "Pie of pizza";
    }
}

And an abstract class that implements it with the same object.

public abstract class PizzaTopper implements PizzaPie{
    private PizzaPie pizza;
    
    @Override
    public String top() {
        return pizza.top();
    }
}

And I have several decorator classes, such as

public class Onions extends PizzaTopper{

    public Onions(PizzaPie pizza) {
        super(pizza);
    }
    
    public String top() {
        return super.top() + topWithOnions();
    }
    
    private String topWithOnions() {
        return " with onions";
    }

And similar classes for peppers, pepperoni, anchovies, pineapple, etc.

I have a list as follows:

List<String> toppings = {onions, pineapple};

Is there a way to take each topping from the toppings list, and use that to create a new pizza with those toppings, to return something like:

Pie of pizza with onions with pineapple

The method would look something like this:


    public PizzaPie CreatePizzaWithUserInput(List<String> toppings) {
            //code
    }
    

And ultimately it would create code that looks like this:

PizzaPie pizza1 = new Onion(new Pineapple(new PizzaPieImplementation()));

In theory this can be done with a lot of ugly if statements but I'm wondering if there's a quicker way of doing it.


Solution

  • You can use Java Reflection to achieve this.

    Here we build our factory with the different possibilities of toppings, then we create a Pizza using createPizzaWithUserInput. In this example we built a pizza with 3 of the four possible toppings. Notice that to add a new Topping possibility you just extends PizzaTopper and add it on the toppingOptions on the PizzaFactory instantiation.

    import java.util.List;
    import java.util.LinkedList;
    
    class Main {
      public static void main(String[] args) {
        // Possibilities
        PizzaFactory pizzaFactory = new PizzaFactory(Onions.class, Bacon.class, Olives.class, Tomatos.class);
    
        // User input
        List<String> toppings = new LinkedList<String>();
        toppings.add("onions");
        toppings.add("olives");
        toppings.add("bacon");
        try{
          PizzaPie pizza = pizzaFactory.createPizzaWithUserInput(toppings);
        System.out.println(pizza.top());
        } catch (Exception e) {
          e.printStackTrace();
        }
        
      }
    }
    

    Here we have the PizzaFactory. We use the class' name to do the matching with the topping option, but you can customize it if you want (to match also "onion"/"onions" or "tomato"/"tomatos" for instance).

    import java.util.List;
    import java.util.Arrays;
    import java.lang.reflect.*;
    
    public class PizzaFactory {
    
      List<Class<? extends PizzaTopper>> toppingOptions;
    
      public PizzaFactory(Class<? extends PizzaTopper>... toppingOptions) {
        this.toppingOptions = Arrays.asList(toppingOptions);
      }
    
      public PizzaPie createPizzaWithUserInput(List<String> toppings) throws NoSuchMethodException, InstantiationException, IllegalAccessException,InvocationTargetException {
        PizzaPie pizza = new PizzaPieImplementation();
        for(String toppingName : toppings) {
           for(Class<? extends PizzaTopper> top : toppingOptions) {
             if(top.getName().toLowerCase().equals(toppingName.toLowerCase())) {
               Constructor<? extends PizzaPie> constructor = top.getConstructor(PizzaPie.class);
               pizza = constructor.newInstance(pizza);
             }
           }       
        }
        return pizza;
      }
    }
    

    Here we have the interface PizzaPie:

    public interface PizzaPie{
      
        String top();
    }
    

    And its implementation.

    public class PizzaPieImplementation implements PizzaPie{
    
        @Override
        public String top() {
            return "Pie of pizza";
        }
    }
    

    The decorator class.

    public abstract class PizzaTopper implements PizzaPie{
        private PizzaPie pizza;
    
        protected PizzaTopper(PizzaPie pizza) {
          this.pizza = pizza;
        }
        
        @Override
        public String top() {
            return pizza.top();
        }
    }
    

    Tomatos topping.

    public class Tomatos extends PizzaTopper{
    
        public Tomatos(PizzaPie pizza) {
            super(pizza);
        }
        
        public String top() {
            return super.top() + topWithOnions();
        }
        
        private String topWithOnions() {
            return " with tomatos";
        }
    }
    

    Onions topping.

    public class Onions extends PizzaTopper{
    
        public Onions(PizzaPie pizza) {
            super(pizza);
        }
        
        public String top() {
            return super.top() + topWithOnions();
        }
        
        private String topWithOnions() {
            return " with onions";
        }
    }
    

    Olives topping

    public class Olives extends PizzaTopper{
    
        public Olives(PizzaPie pizza) {
            super(pizza);
        }
        
        public String top() {
            return super.top() + topWithOnions();
        }
        
        private String topWithOnions() {
            return " with olives";
        }
    }
    

    Bacon topping.

    public class Bacon extends PizzaTopper{
    
        public Bacon(PizzaPie pizza) {
            super(pizza);
        }
        
        public String top() {
            return super.top() + topWithOnions();
        }
        
        private String topWithOnions() {
            return " with bacon";
        }
    }
    

    Output of the test! ("Pie of pizza with onions with olives with bacon")