Search code examples
javaarchitecturerandomcoding-stylearrayobject

Better way to call common method on randomly selected object in java


Nice day to everybody.

I have an abstract class with the method runRandomExercise(), and several classes that extends it to add different kind of exercise.

I now want to chose a random type exercise, so I need to randomly choose one of the classes, and call runRandomExercise() on that.

For now I am manually coding this, which is not the very best solution I think. However, I can’t store just the classes in the array since the class type is different, and if I use object[] I can’t call the runRandomExercise() method. Any smart way to handle this?

Here is my code till now. It works, but it’s gonna be a pain to add other classes...

/*Specific classes that extend abstract class TrainingClass with the runRandomExercise() method*/
private MatheMagic mMathMag;
private Mnemonics mMnemonics;
private String[] mTrainingClasses;


/*Initialize classes*/
mMathMag = new MatheMagic();
mMnemonics = new Mnemonics();

/*Manually store classe names*/
mTrainingClasses = new String[2];
mTrainingClasses[0] = "mMathMag";
mTrainingClasses[1] = "mMnemonics"; 


/*Return random exercise*/
public String[] RandomExercise() {
    Random aGenerator = new Random();

    /*Get random class name*/
    int rnd = aGenerator.nextInt(mTrainingClasses.length);
    String aChosen = mTrainingClasses[rnd];

    String[] aRes = new String[2];


    if (aChosen == "mMathMag") {
        aRes = mMathMag.runRandomExercise();
    } else if (aChosen == "mMnemonics") {
        aRes = mMnemonics.runRandomExercise();
    }

    return aRes;
}

EDIT Here is how TrainingClass is defined:

/** Common interface for all exercises */ 
public interface Exercise { 
   public String[] run();
}

/** Common interface for all training classes */
 public abstract class TrainingClass {

  private Random mRandGen = new Random();
  public ArrayList<Exercise> mExerciseTypes = new ArrayList<Exercise>();

  /** Run a random exercise */
 public String[] runRandomExercise() {
    int i = mRandGen.nextInt(mExerciseTypes.size());
    return mExerciseTypes.get(i).run();
 }
}



/*Specific training class*/

public class MatheMagic extends TrainingClass {

 public MatheMagic() {

    class SomeExercise implements Exercise {

        public String[] run() {

             String[] mRes = new String[2];
              mRes[0] = "Question type 1";
              mRes[1] = "Answer type 1";
            return mRes;
        }

    }

    class SomeOtherExercise implements Exercise {

        public String[] run() {

             String[] mRes = new String[2];
              mRes[0] = "Question type 2";
              mRes[1] = "Answer type 2";
            return mRes;
        }

    }
    SomeExercise mN = new SomeExercise();

    SomeOtherExercise mS = new SomeOtherExercise();

    mExerciseTypes.add(mN);
    mExerciseTypes.add(mS);
 } 

}

Solution

  • Easy solution is to create an interface with the common method and have all your classes extend it.

    Create a collection or array of that type instead of Object; you can simply iterate through or randomly select and call the method you want.

    It feels like a Command pattern from GoF to me.

    public interface Exercise {
        void execute();
    }
    

    Now your classes do this:

    public class MatheMagic implements Execise {
        public void execute() {
            // special logic here.
        }
    }
    

    Then you can do this:

    int numExercises = 1;
    Exercise [] exercises = new Exercise[numExercises];
    exercises[0] = new MatheMagic();
    for (Exercise exercise : exercises) {
        exercise.execute();
    }