1

I have a problem with a program in Java. My program have an interface Bonus and nine subclasses that extend Bonus. I have to generate a random instance of Bonus and I can't use prototype pattern because every bonus has a quantity that is random, so I have to create a bonus every time I need it.

Switch statement and if else is not the solution because I have too many subclasses and the code will be very long and rough.

So I decide to use reflection and it goes very well for me, but my professor say to me that reflection is a bad trick because is not type-safe and every time I change the name of bonus subclasses I have to change the code in my class GenerateBonus.

I searched on internet and I didn't find a solution for me. So anyone know an alternative to reflection to generate object of random subclasses?

Filburt
  • 17,626
  • 12
  • 64
  • 115
  • "every bonus has a quantity that is random" -- Does that mean in the constructor the `Random` class is used? – OneCricketeer May 10 '16 at 21:45
  • Why do you need to generate objects of random subclasses? That is a very unusual requirement. It screams [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). What are you trying to accomplish by doing this? – John Kugelman May 10 '16 at 21:47
  • How about an enum that specifies which subclass gets instantiated based on a random number? – Richard Barker May 10 '16 at 21:50
  • Yes I use the Random in the constructor. – Stefano Valladares May 10 '16 at 21:51
  • See the given answer; that will help you solve your problem. – Richard Barker May 10 '16 at 21:51
  • Enum is not the solution for me because is not extensible. – Stefano Valladares May 10 '16 at 21:52
  • Talkink about Java I would go for factory objects. – Tamas Hegedus May 10 '16 at 21:53
  • Btw, I'm not sure how you intended to use reflection, but I guess it wasn't for getting all the implementing classes of `Bonus`. That wouldn't be [an easy problem](http://stackoverflow.com/q/492184/2032064). – Mifeet May 10 '16 at 22:05
  • Do you really need to have different subclasses for each bonus? Does every bonus has a different *behavior*? Or just different values of some parameters? If it's the latter, you could encode the values in an enum, e.g., and make things easier for you. – Mifeet May 10 '16 at 22:11

2 Answers2

3

You need two things:

  1. if you want to pick a random item (a sublass in your case), there has to be some place which contains information about all possible items.
  2. a way how to create the desired instance once you pick the subclass.

For creation, I would use a Supplier or a factory if you cannot use prototype. Here is an example with suppliers:

    ArrayList<Supplier<Bonus>> bonuses = new ArrayList<>();
    bonuses.add(Bonus1::new);
    // ...
    bonuses.add(Bonus9::new);
    int randomNumber = generateRandomNumber(bonuses.size());
    Bonus randomBonus = bonuses.get(randomNumber).get();

For the first problem, i.e. getting the list of all possible bonuses, you can simply add them to a collection as in the example above, create an enum which contains all values, etc.

Here is an enum example:

enum AllBonuses{
    Bonus1(BonusClass1::new),
    // ...
    Bonus9(BonusClass9::new);

    private final Supplier<Bonus> bonus;
    AllBonuses(Supplier<Bonus> bonus) { this.bonus = bonus; }
    public Bonus create() { return bonus.get(); }
}

// Usage:
int randomNumber = generateRandomNumber(AllBonuses.values().length);
Bonus randomBonus = AllBonuses.values()[randomNumber].create();

I wouldn't use reflection. Unless there's no other way, reflecation is rarely a good solution.


Update: If you couldn't use a supplier for some reason (e.g. you don't have Java 8), you can use the good old factory pattern:

class Bonus1Factory implements BonusFactory {
    @Override
    public Bonus create() { return new BonusClass1(); }
}
// ...
ArrayList<BonusFactory> bonuses = new ArrayList<>();
bonuses.add(new Bonus1Factory());
// ...
Mifeet
  • 12,949
  • 5
  • 60
  • 108
1

With 9 subclasses I would use a switch statment

int randomSubClass = getRandomNumber();
switch(randomSubClass) {
   case 1:
     return generateRandomFunBonus();
   case 2:
     return generateRandomHardBonus();
   ...
   default:
     throw new Exception(":(");
}

As an alternative to switching on an int you could have an enum with one entry for each subclass and an overriden create method.

David Waters
  • 11,979
  • 7
  • 41
  • 76
  • 1
    Note that you'll want to create a minimum and maximum for the randomly generated number. the default case, unless it creates a default class, should never be hit. – Richard Barker May 10 '16 at 21:52
  • Yes this is for me the best solution in alternative to reflection, but is not work well for my professor. – Stefano Valladares May 10 '16 at 21:58
  • 1
    @RichardBarker Yep, the default case should never be hit, thats why it throws an exception. Two reasons for including it, firstly because I have debugged too many errors in code that "//Should never reach here", secondly the compiler does not know that you have all the possible options covered so without the default clause will complain the method does not always return a value. – David Waters May 10 '16 at 22:00