Home > Blockchain >  How do I get rid of a ever growing switch-statement?
How do I get rid of a ever growing switch-statement?

Time:10-01

The Problem

switch (choice) {
    case 0:
        return new Class0();
    case 1:
        return new Class1();
    case 2:
        return new Class2();
    case <n - 1>:
        return new Class<n - 1>();
    default
        return new Class<n>();
}

What I Tried

Chain of responsibility design pattern. A great and amazing solution for distributing complex tasks. However, I don't want a class that checks if it can do 1 method.


Motivation

I'm tired of updating the code every time I add something. Bad design.

CodePudding user response:

Providing that each of Class0, Class1, ...ClassN have public non-arg constructor, Class::forName and Class::newInstance may be used to build a class name, retrieve a class, and instantiate it:

public static final int MAX_CLASS_N = 5; // n limit

public Object getInstance(int x) throws Exception {
    int id = x < 0 || x >= MAX_CLASS_N ? MAX_CLASS_N : x;
    return Class.forName("Class"   id).newInstance();
}

Similarly, an array/list of Class can be created as a "cache", a helper method replacing a checked ClassNotFound exception with a runtime IllegalArgumentException is needed:

public static Class<?> classForName(String clazz) {
    try { 
        return Class.forName(clazz); 
    } catch (ClassNotFoundException cex) {
        throw new IllegalArgumentException("Bad class name: "   clazz, cex);
    }
}

public static final Class[] BUILDERS = IntStream.rangeClosed(0, MAX_CLASS_N)
        .mapToObj(i -> "Class"   i)
        .map(MyClass::classForName)
        .toArray(Class[]::new);

public static Object buildInstance(int n) throws Exception {
    int id = n < 0 || n >= MAX_CLASS_N ? MAX_CLASS_N : n;

    return BUILDERS[id].newInstance();
}

Tests (assuming classes Class0, Class1, ... Class5 are defined)

System.out.println(getInstance(-1));
System.out.println(getInstance(2));
System.out.println(getInstance(5));
System.out.println("----");
System.out.println(buildInstance(0));
System.out.println(buildInstance(3));
System.out.println(buildInstance(50));

Output:

Class5@445b84c0
Class2@61a52fbd
Class5@233c0b17
----
Class0@63d4e2ba
Class3@7bb11784
Class5@33a10788

CodePudding user response:

In this example we use instead of the switch expression an enum representing the implementing a class supplier:

  interface ProblemSolver{}
  
  public enum Choice implements Supplier<ProblemSolver> {
    ONE(AnyProblemSolver::new),
    TWO(AnyProblemSolver::new)
    ;
    
    private final Supplier<ProblemSolver> supplier;
    
    Choice(Supplier<ProblemSolver> supplier){
      this.supplier = supplier;
    }

    @Override
    public ProblemSolver get() {
      return supplier.get();
    }
  }

  public ProblemSolver getProblemSolver(Choice choice){
    return choice.get();
  }
  • Related