Viktor Odekolonovich
Viktor Odekolonovich

Reputation: 11

How to simplify huge switch-case expression?

I have got some troubles with next code. I have simple interface like:

public interface Game {
    int start();
}

Many classes that implements this interface like:

public class FirstGame implements Game {
    public static final int ID = 1;
    
    @Override
    int start() {
        // Do something and return result
    }
}

And GameManager class that has one method like this:

public Game getGameById(int id) {
    switch(id) {
        case FirstGame.ID:
            return new FirstGame();
        case SecondGame.ID:
            return new SecondGame();
        // ..... and many other cases....
    }
    return null;
}

I was trying to simplify this switch-case construction using reflection like this: Annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface GameId {

long value() default 0;
}

FirstGame class:

@GameId(value = 1)
public class FirstGame implements Game {
    public static final int ID = 1;
    
    @Override
    int start() {
        // Do something and return result
    }
}

And GameManager method like this:

public Game getGameById(int id) {
    Game game = null;
    try {
        Reflections reflections = new Reflections();
        for (Class<?> clazz : reflections.getTypesAnnotatedWith(GameId.class)) {
            if (clazz.getAnnotation(GameId.class).value() == id) {
                Constructor constructor = clazz.getConstructor();
                game = (Game) constructor.newInstance();
                break;
            }
        }
    } catch (Exception ex) { ex.printStackTrace();}
    return game;
}

But it works too slow. So, how to simplify switch-case expression in some other way? Thanks and sorry for broken English.

Upvotes: 0

Views: 318

Answers (2)

Viktor Odekolonovich
Viktor Odekolonovich

Reputation: 11

Thanks to Holger's for his answer. My method with reflection was so close to success. It just needs to add all constructors to HashMap once where ID is used as key. So now my code of GameManager looks like:

Map<Integer, Constructor> constructorMap = new HashMap<>();

private void fillConstructorMap() {
    try {
        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.addUrls(ClasspathHelper.forPackage("com.my.package.name"));
        Reflections reflections = new Reflections(configurationBuilder);
        for (Class<?> clazz : reflections.getTypesAnnotatedWith(GameId.class)) {
            Integer id = clazz.getAnnotation(GameId.class).value();
            Constructor constructor = clazz.getConstructor();
            constructorMap.put(id, constructor);
        }
    } catch (NoSuchMethodException ex) {
        ex.printStackTrace();
    }
}

public Game getGameById(int id) {
    Game game = null;
    try {
        game = (Game) constructorMap.get(id).newInstance();
    } catch (Exception ex) {ex.printStackTrace();}
    return game;
}

This method works so fast as method with switch-case expression.

Upvotes: 1

user4910279
user4910279

Reputation:

How about this?

static final List<Supplier<Game>> GAMES = List.of(
    FirstGame::new,
    SecondGame::new
    // ...
);

public Game getGameById(int id) {
    return GAMES.get(id).get();
}

Or

static final Map<Integer, Supplier<Game>> GAMES = Map.of(
    1, FirstGame::new,
    2, SecondGame::new
);

public Game getGameById(int id) {
    return GAMES.get(id).get();
}

Upvotes: 1

Related Questions