0

Task: have a class that implements something in different ways. User of class should only see public enum that represents available options, while hiding all implementation of different behavior.

To avoid checking the provided "style" on every call of method, constructor uses switch on enum value provided to assign appropriate inner private class to a field.

Here is SSCCE:

public class Greeter {
    public enum GreetingStyle { HEY, HELLO }

    private String name;
    private GreetingChooser greetingChooser;
    public Greeter(String name, GreetingStyle style) {
        this.name = name;
        switch(style) {
            case HEY:
                greetingChooser = new Hey();
                break;
            case HELLO:
                greetingChooser = new Hello();
                break;
            default :
                throw new UnsupportedOperationException("GreetingStyle value not handled : " + style.toString());
        }
    }

    public void greet() {
        // need to avoid switch(style) here
        System.out.println(greetingChooser.greeting() + ", " + name + "!");
    }

    // this interface can't be public
    private interface GreetingChooser {
        String greeting();
    }

    private class Hey implements GreetingChooser {
        public String greeting() {
            return "Hey";
        }
    }

    private class Hello implements GreetingChooser {
        public String greeting() {
            return "Hello";
        }
    }

    public static void main(String args[]) {
        new Greeter("John Doe", Greeter.GreetingStyle.HEY).greet();
        new Greeter("John Doe", Greeter.GreetingStyle.HELLO).greet();
    }
}

Question: is it a good way to implement such a functionality to make it maintainable in the future (e.g. we'll need to add GreetingStyle ALOHA)? Another idea I had was to use a static map

private static final Map<GreetingStyle, GreetingChooser> greetingMap;
static {
    greetingMap = new HashMap<>();
    greetingMap.put(Greeter.GreetingStyle.HEY, new Hey());
    greetingMap.put(Greeter.GreetingStyle.HELLO, new Hello());
}

and to use greetingMap.get(style) in constructor.

Note: in Java 8 it would be probably best implemented with lambdas (if interface only has one function), but I'm constrained to Java 7.

andrybak
  • 2,129
  • 2
  • 20
  • 40
  • 1
    You could put `greet` as abstract in the `enum`, and ends up with `GreetingStyle.HEY.greet("JohnDoe")`, see this [answer](http://stackoverflow.com/a/7414053/180100) for example –  Nov 06 '15 at 11:55

2 Answers2

2

Instead of just using the enum as a constant for the factory switch, you could avoid the switch by equipping the enum constant with either configuration

public enum GreetingStyle 
{ 
    HEY("Hey"),
    HELLO("Hello");

    GreetingStyle(String text) {
        this.text = text;
    }

    public final String text;
}

or behaviour:

public enum GreetingStyle 
{ 
    HEY {
        public void greet() { /* performs hey style greeting*/ }
    },
    HELLO{
        public void greet() { /* performs hello style greeting*/ }
    };

    public abstract void greet();
}
wero
  • 32,544
  • 3
  • 59
  • 84
  • But this would make `greet` public. Java doesn't seem to allow private abstract methods inside enums. – andrybak Nov 06 '15 at 12:05
  • @andrybak that may or may not be a problem, depending on the case. – wero Nov 06 '15 at 12:12
  • still weird that Java would allow private methods inside inner classes which are accessible to outer class, but wouldn't allow private methods in inner enums. **Edit**: the issue is that we can't override private methods in subclasses, and enums are only a syntax sugar to build hidden class hierarchy. – andrybak Nov 06 '15 at 12:18
0

Why are you bound to the enum? What you describe can be easily achieved using polymorphism. If you worry about extending it in the future, you can use design patterns such as Factory or Decorator.

Nano
  • 36
  • 3