mardi 28 avril 2020

Implementing Pattern in Java to Call Method Dynamically at Runtime

I am trying to wrap my head around the implementation of a Strategy pattern in Java, as I believe it is the implementation I need to get the desired behavior. I have a method called chooseAction() that accepts a single parameter that denotes the state of the world at a specific time. Let's say it contains information about the current level of traffic and the weather, as well as the current time. Depending on the Time enum and a Person's availableActions list, a Person can perform different actions. However, each action needs its own individual choose method that returns a boolean denoting whether or not they will do it. A Person would have different reasons to choose whether or not to go to work than brush their teeth.

This is what I currently have.

public enum Action{
GO_TO_WORK{
    @Override
    public TimeList getTimes() {
        return new TimeList(Time.MORNING);
    }, 
BRUSH_TEETH{
    @Override
    public Time getTimes() {

        return new TimeList(Time.MORNING, Time.NIGHT);
    }, 
WATCH_TV{
    @Override
    public Time getTimes() {
        return new TimeList(Time.MORNING, Time.EVENING);
    };
}

public class Person{

final static List<Action> availableActions; //Contains an unchanging list of actions a Person can do 

public List<Action> chooseAction(WorldState worldState)
{
     Time time = worldState.getTime(); //Get the current time
     List<Action> validActions = getAvailableActions(time); //Actions the Person can do now
     List<Action> chosenActions = 
     for (Action a : validActions) {
        switch (a) {
        case BRUSH_TEETH:
            if (chooseToBrushTeeth(this, worldState))
                chosenActions.add(a);
            break;
        case GO_TO_WORK:
            if (chooseToGoToWork(this, worldState))
                chosenActions.add(a);
            break;
        case WATCH_TV:
            if (chooseToWatchTV(this, worldState))
                chosenActions.add(a);
            break;
        default:
        }
    }
    return chosenActions;
}
public List<Action> getAvailableActions(Time time)
{ 
     List<Action> validActions = new ArrayList<>();
     for(Action a: availableActions)
     {
        if(a.getTimes().contains(time))
        {
           validActions.add(a);
        }
     }
     return validActions;
}

public enum Time{

MORNING, AFTERNOON, EVENING, NIGHT;

}

public class TimeList{

public TimeList(Time... time) {
    for (Time t : times) {
        this.add(t);
    }
}

The individual choose methods return either true or false depending on the parameters of the state of the Person and the WorldState object. The exact specifications of these methods and the WorldState class are unimportant to this problem. What I'm trying to do is apply a Strategy pattern or a similar design pattern so that I can remove the inefficient switch/case and much of the redundancy. The tricky part is that I cannot simply use inheritance to make different objects for each Action and have them call their own choose method. This is because the Actions do not make the choice, the Person does. I want to design it so that, for instance, I can have a LazyPerson and an ActivePerson who can potentially choose differently when GO_TO_WORK is a valid action, even if the WorldState is the same.

I think I'm close to understanding it, but I'm not quite there yet. Any help would be greatly appreciated.

Aucun commentaire:

Enregistrer un commentaire