简体   繁体   中英

Strategy pattern with inner enum

I'm trying to get rid of big switch statement from my code and I thought that Strategy pattern based on my existing enum would be nice. The concept is like:

public class MyStrategy {

    public MyStrategy() {
        Option.Option1.setMethodToExecute(this::action1);
        Option.Option2.setMethodToExecute(this::action2);
    }

    public void executeChoosenMethod(int i) {
        Option.values()[i].execute();
//        instead of
//        switch(convertItoOption()) {
//            case Option1:...
//            case Option2:...
//        }
    }

    private void action1() {
        System.out.println("action1");
    }

    private void action2() {
        System.out.println("action2");
    }

    private enum Option {
        Option1, Option2;

        private InvokeAction methodToExecute;

        public void setMethodToExecute(InvokeAction methodToExecute) {
            this.methodToExecute = methodToExecute;
        }

        public void execute() {
            methodToExecute.execute();
        }
    }

    @FunctionalInterface
    private interface InvokeAction {
        void execute();
    }
}

so I can use it like:

public class StrategyTest {
    public static void main(String[] args) {
        MyStrategy strategy = new MyStrategy();
        //user choose 0 or 1
        strategy.executeChoosenMethod(0);
        strategy.executeChoosenMethod(1);
    }
}

but I don't like this part with Option.Option1.setMethodToExecute(this::action1); since my enum has more and more options and I would like to have all of this inside enum. What would be perfect is something like this:

public class MyStrategy {
    public void executeChoosenMethod(int i) {
        Option.values()[i].execute();
    }

    private void action1() {
        System.out.println("action1");
    }

    private void action2() {
        System.out.println("action2");
    }

    private enum Option {
        Option1(MyStrategy.this::action1), 
        Option2(MyStrategy.this::action2);

        private InvokeAction methodToExecute;

        private Option(InvokeAction method) {
            methodToExecute = method;
        }

        public void execute() {
            methodToExecute.execute();
        }
    }

    @FunctionalInterface
    private interface InvokeAction {
        void execute();
    }
}

but this is impossible since enum is static and I don't have access to enclosing instance by MyStrategy.this. I need enum, because I have set of options and it is convenient to use methods like values() or valueOf(), but what I would like to have is single line invoke instead of growing switch. Do you have any ideas how to achieve sometghing like this or is there any workaround to make this enum constructor call possible Option1(MyStrategy.this::action1) ?

With enums you could implement it like this:

public class MyStrategy {
    public void executeChoosenMethod(int i) {
        Option.values()[i].execute(this);
    }

    private void action1() {
        System.out.println("action1");
    }

    private void action2() {
        System.out.println("action2");
    }

    private enum Option {
        Option1(MyStrategy::action1), 
        Option2(MyStrategy::action2);

        private InvokeAction methodToExecute;

        private Option(InvokeAction method) {
            methodToExecute = method;
        }

        public void execute(MyStrategy s) {
            methodToExecute.execute(s);
        }
    }

    @FunctionalInterface
    private interface InvokeAction {
        void execute(MyStrategy s);
    }
}

This uses the fact the with lambdas you can make method references to arbitrary instance methods and call them on a specific instance by passing in the instance as first parameter.

you're right. This isn't possible with enum. But why not just use a good old class:

public class MyStrategy {

    public MyStrategy() {
        buildUp();
    }

    public void executeChoosenMethod(int i) {
        actions.get(i).execute();
    }

    private void action1() {
        System.out.println("action1");
    }

    private void action2() {
        System.out.println("action2");
    }

    private List<InvokeAction> actions = new ArrayList<>();

    private void buildUp() {
        actions.add(this::action1);
        actions.add(this::action2);
    }

    @FunctionalInterface
    private interface InvokeAction {
        void execute();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM