简体   繁体   中英

Better way to instantiate multiple different classes in Java via factories

Say you have a lot of similar classes that may get instantiated at any point. One obvious way to write this would be a whole slew of conditionals:

public static Common factory(String key, InstantiationObj instantiationObj) {

  if( key == "A") {
    return new TypeA(instantiationObj);
  }
  else if( key == "B") {
    return new TypeB(instantiationObj);
  }
  else if( key == "C" ) {
    return new TypeC(instantiationObj);
  } 
  else if( key == "D" ) {
    return new TypeD(instantiationObj);
  }
  else {
    return new DefaultClass(instantiationObj);
  }

}

I like to avoid all the conditionals though. In Java I got this working:

public static <T extends Common> T factory(String key, InstantiationObj instantiationObj) throws Exception {  
  Class[] cArg = new Class[1];
  cArg[0] = InstantiationObj.class;
  HashMap<String, Class> potentialClasses = new HashMap<>();
  
  potentialClasses.put("A", TypeA.class);
  potentialClasses.put("B", TypeB.class);
  potentialClasses.put("C", TypeC.class);
  potentialClasses.put("D", TypeD.class);

  Class<T> classType = potentialClasses.getOrDefault(key, DefaultClass.class);
  return classType.getDeclaredConstructor(cArg).newInstance(instantiationObj);
}

This is much easier to test, but it still looks clunky to me especially the block to determine the right constructor. Does Java have a more streamlined way of executing this pattern?

One suggestion can be

public class CommonClass {
    private static HashMap<String, Class> potentialClasses = new HashMap<>();
    static { 
        potentialClasses.put("A", TypeA.class);
        potentialClasses.put("B", TypeB.class);
        potentialClasses.put("C", TypeC.class);
        potentialClasses.put("D", TypeD.class);
    }

    public static <T extends Common> T factory(String key) throws Exception {  
        Class[] cArg = new Class[1];
        cArg[0] = NecessaryConstructor.class;

        Class<T> classType = potentialClasses.getOrDefault(key, DefaultClass.class);
        return classType.getDeclaredConstructor(cArg).newInstance(descriptor);
   }

One way I like implementing the factory pattern is with using spring. You can have as many classes of a specific type in your project.

Then, inject or autowire a List of same type in a class and Spring will set all instances of that type in the list.

Finally, transform the list to a map like you have and you will have maximized the idea of “Closed for modification, open for extensibility” on you factory and project.

In case the logic is so simple as in your case, you may use reflection, without defining mapping:

public static Common factory(String key, InstantiationObj instantiationObj) {
    String className = "Type" + key;
    Class<T> classType = Class.forName(className);
    ...

Thus, for key "A" the variable className will have value "TypeA", for key "B" it will have value "TypeB", etc.

But this will work in this simple case only, when mapping of parameter key to class names follows a simple pattern.

In case the logic becomes more complex later on, you would need if (...) clauses for every condition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM