3
\$\begingroup\$

I was wondering what would be the most appealing way of achieving this goal.

I have a blackbox. I give it a supported type and it returns a random value base off of the type.

For my supported types, I have an enum defined as follows:

public enum Types 
{
    INTEGER,
    DOUBLE,
    BIGINTEGER,
    BIGDECIMAL,
    STRING,
    BOOLEAN,
    CHAR
}

I thought the easiest way would be to create a static function which returns an Object based off of the Type

public static Object getRandomValue(Types type)
{
    switch(type)
    {
        case INTEGER:
            return randomInt();
        case DOUBLE:
            return randomDouble();
        case BIGINTEGER:
            return randomBigInt();
        case BIGDECIMAL:
            return randomBigDecimal();
        case STRING:
            return randomString();
        case BOOLEAN:
            return randomBool();
        case CHAR:
            return randomChar();
    }
}

The problem is that an additional cast would have to be made each time that I want to retrieve a random value based off of this method.

I've looked into the design patterns abstract factory and factory, and I can't decide if there's an advantage into implementing either of those. Or if there's a different design pattern that seems to be more appropriate.

Assume that all my random methods are defined.

\$\endgroup\$
1

2 Answers 2

4
\$\begingroup\$

Firstly if you are going to be passing objects of type Object around I guess you best be prepared to do a few casts here and there. You can achieve this using a simple interface and templates quite easily.

Interface:

public interface RandomGenerator <T> {
    T getRandom();
}

Implementation:

public class DoubleRandomGenerator implements RandomGenerator<Double> {

    @Override
    public Double getRandom() {
        //do what you do
        return 0d;
    }
}

Next, don't try to return every type of Object from the one Switch statement, it will always be ugly. In order to work out the next step maybe you could elaborate on what is happening outside the blackbox, e.g. if the calling code wants a Double you could add that into your enum:

public enum Types 
{
    INTEGER(new IntegerRandomGenerator()),
    DOUBLE(new DoubleRandomGenerator()),
    BIGINTEGER(...),
    BIGDECIMAL(...),
    STRING(...),
    BOOLEAN(...),
    CHAR(...);

    private RandomGenerator generator;

    private Types(RandomGenerator generator) {
         this.generator = generator;
    }

    public RandomGenerator getGenerator() {
         return generator;
    }
}

The main difference here is that you have deferred the creation of the random value to the callers end. The calling code will also be required to either use generics too:

RandomGenerator<Double> g = Types.DOUBLE.getGenerator();
Double d = g.getRandom();

Or generate warnings and cast the result:

RandomGenerator g = Types.DOUBLE.getGenerator();
Double d = (Double)g.getRandom();

Finally, you say you have read about the factory pattern, that is ultimately what you are playing with here.

Note. I think Enum naming should use the singular (Type) rather than the plural (Types), you are not selecting an Object of type Types as your Object (Types.DOUBLE) represents just one type.

\$\endgroup\$
2
  • \$\begingroup\$ This will still require some casting in the calling code, won't it? Either you have to cast the returned RandomGenerator itself, or the result of the call to the generator. \$\endgroup\$
    – tobias_k
    Sep 12, 2013 at 15:22
  • \$\begingroup\$ Yes, I'll clarify my comment that meant exactly that, the code is neater but you still require either a cast or to add the generic type at the calling end. \$\endgroup\$
    – JohnMark13
    Sep 12, 2013 at 15:27
3
\$\begingroup\$

1) I don't think you need to define an enum since you can use Boolean.class, Integer.class, etc.

2)

public static <T> T getRandom(Class<T> classs) {
  if (classs.equals(Boolean.class)) {
      return (T) randomBool();
  } else if (classs.equals(Integer.class) {
      return (T) randomInt();
  // etc.
  } else
      // Edit: original "else" was: "return null;" changed to:
      throw new IllegalStateException("Cannot generate a random " + classs.toString());
}

EDIT
There is also the solution below, but that would still require casting:

   public enum Type {
    INTEGER {
        @Override public Integer getRandom() {
            return new randomInt();
        }
    }; // BOOLEAN, etc. 

    public abstract Object getRandom();
}

That is basically the same as the original OP code, but where the random method call is within the enum instead of being in a separate method with a switch-statement.

\$\endgroup\$
6
  • \$\begingroup\$ The purpose of the enum is enforce that the function only exists for certain types. \$\endgroup\$
    – Rhs
    Sep 12, 2013 at 15:14
  • 1
    \$\begingroup\$ Then instead of returning null, you could throw an exception when the class is of an unknown type. \$\endgroup\$
    – toto2
    Sep 12, 2013 at 15:17
  • \$\begingroup\$ @toto2 This is exactly what I was writing up, including the Exception... +1 \$\endgroup\$
    – tobias_k
    Sep 12, 2013 at 15:18
  • \$\begingroup\$ Your second variant requires casting in the calling code again. I liked your (our) first variant better... \$\endgroup\$
    – tobias_k
    Sep 12, 2013 at 15:26
  • \$\begingroup\$ @tobias_k Yes, I added that fact in the text. Thanks. \$\endgroup\$
    – toto2
    Sep 12, 2013 at 15:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.