简体   繁体   中英

using setter to inject service into Enum…Bad Practice?

Is this considered bad practice? Essentially, based on the enum I want to call a specific method in an interface. Each enum will have its own interface implementation (WalkImpl,RunImpl,JogIMpl, etc....) all based off of the ActivityService interface. I just wanted to know is this the right way to "inject" a service into an Enum. I am doing it this way since I can't autowire the service. Thanks

@Component
public class HelloWorldImpl implements HelloWorld {

private enum MyEnum{

    WALK {
        @Override
         public void execute() {
            System.out.println("I am walking");
            activityService.doSomethingWithMe(this.name());
        }
    },
    RUN{
        @Override
        public void execute() {
            System.out.println("I am running");
        }
    },JOG{
        @Override
        public void execute() {
            System.out.println("I am jogging!");
        }
    }, SKIP{
        @Override
        public void execute() {
            System.out.println("I am skipping!");
        }
    };

    public abstract void execute();

    private static ActivityService activityService;

    public void setActivityService(ActivityService activityService) {
        this.activityService = activityService;
    }
}


@Autowired
ActivityService activityService;


 @Override
 public void executeMe(){
    MyEnum myEnum = MyEnum.WALK;
    myEnum.setActivityService(activityService);
    myEnum.execute();

  }
}

Do not use enums like that. When I have more time I will explain but most programmers expect and even Java the language somewhat expects enums to be idempotent and immutable.

All member variables in an enum should be final and an enum should not produce side effects. This is because enums are a form (albeit crappy) of functional programming style dispatch. That is enums should be treated as symbols and not objects (even though they are singleton objects).

Also do not use enums to solve the singleton pattern unless you are following the functional rules from above. Here is how I would probably right your given code:

@Component
public class HelloWorldImpl implements HelloWorld {

    private enum MyEnum{
        //Notice the final here 
        private final String message;
        WALK ("I am walking"),
        RUN("I am running"),
        JOG("I am jogging!"),
        SKIP("I am skipping!");

        public MyEnum(String message) { this.message = message; }

        public String getMessage() { return this.message; }

    }        

    @Autowired
    ActivityService activityService;

    @Override
    public void executeMe() {
       MyEnum myEnum = MyEnum.WALK;
       _executeMe(myEnum);
    }

    void _executeMe(MyEnum m) {
        //switch or if on the enums you want to 
        //do stuff on with the activity service.
        System.out.println(m.getMessage());
        if (m == MyEnum.WALK)
           activityService.doSomethingWithMe(m.name());
    }
}

Enums are best used for things that MUST be differentiated in code--business logic. If you are using them for data (as your example), it doesn't make any sense to me.

Also, by data vs code I'm not talking about simply iterating over them, you actually have to have significantly different code USING different enums, otherwise they are just a (bad) data initialization device.

A better initialization of that type of data might be:

String[] init=new String[] {"WALK", "I am walking", "SKIP", "I am skipping", ...}
Map lookup=new HashMap();
for(int i=0;i+=2;i<init.length)
{
    lookup.put(init[i],init[i+1])
}

No redundancy, much simpler, and when that list becomes more complicated it's trivial to take it outside of the code to a text, properties, xml or whatever flavor of data you prefer.

You can even associate code with these if that is what you are after by wrapping "Lookup" and this entire initialization into an object (A good idea) I'd make something that looked like this:

public class Motivate() 
{

    private static Map<String, Motivate> motivations;
    private String action;
    private String description;

    private Motivate(String action, String description)
    {
        this.action=action;
        this.description=description;
    }
    public void init()
    {
        if(motivations == null)
        {
            build motivations using all the stuff in the first example
        }
    }
}

If you want different code attached (assuming your examples were just trivial and each "Mode" needed different code), add a member that holds an interface like "Runnable" and pass that into the constructor when you build them.

Then your code should never refer to "RUN" or "WALK", it is just data that is bound, for instance, to a users keystroke or some other data.

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