Creating instances using one generic factory method

I am trying to find an easy way to create objects at runtime based on a static attribute of the String class called NAME.

How can I improve this code that uses a simple if construct?

public class FlowerFactory { private final Garden g; public FlowerFactory(Garden g) { this.g = g; } public Flower createFlower(final String name) { Flower result = null; if (Rose.NAME.equals(name)) { result = new Rose(g); } else if (Oleander.NAME.equals(name)) { result = new Oleander(g); } else if ... { ... } ... return result; } 

newInstance () cannot be used for these classes unless I remove the constructor argument. Should I build a map (Map) of all supported links of the flower class and move the contructor argument to the property setting method, or are there other simple solutions?

Background information: my goal is to implement some kind of "self-registration" of new color classes, FlowerFactory.getInstance().register(this.NAME, this.class) , which means that out of very good answers, solutions based on introspection have so far corresponded would be the best.

+4
source share
7 answers

You can use reflection, despite having a constructor argument:

 Rose.class.getConstructor(Garden.class).newInstance(g); 

In combination with matching a static name and a class, this can be implemented as follows:

 // TODO handle unknown name FLOWERS.get(name).getConstructor(Garden.class).newInstance(g); 

where flowers can be filled in a static initialization block:

 static { Map<String, Class<? extends Flower>> map = new HashMap<String, Class<? extends Flower>>(); map.put(Rose.NAME, Rose.class); // add all flowers FLOWERS = Collections.unmodifieableMap(map); } 
+2
source

One possibility is to use an enumeration. At the simplest level, you can replace constants of type Rose.NAME with enumeration values ​​and preserve the internal mapping between enum values ​​and classes to instantiate:

 public enum Flowers { ROSE(Rose.class), OLEANDER(Oleander.class); private final Class<? extends Flower> flowerClass; Flowers(Class<? extends Flower> flowerClass) { this.flowerClass = flowerClass; } public Flower getFlower() { Flower flower = null; try { flower = flowerClass.newInstance(); } catch (InstantiationException e) { // This should not happen assert false; } catch (IllegalAccessException e) { // This should not happen assert false; } return flower; } } 

Since flower class classes do not have a default constructor, Class.newInstance() cannot be used, so instantiating a class through reflection is a little more cumbersome (although possible). An alternative would be to use Prototype to create a new instance of colors.

This already ensures that you always keep the binding between possible color names and actual flower classes in sync. When you add a new color class, you must create a new enum value that includes matching to create new instances of the class. However, the problem with enum aproach is that the Garden instance that you are using is committed at startup. (If you do not pass it as the getFlower() parameter, but then there is a risk of losing consistency, i.e. it is more difficult to ensure that a certain group of colors is created in a certain garden).

If you want to be more flexible, you can use Spring to move the entire mapping between names and concrete (bean) classes to the configuration file. Then your factory just loads the ContextText Spring in the background and uses the display it displays. Whenever you introduce a new color subclass, you just need to add a new line to the configuration file. Again, this approach, in its simplest form, requires you to install the Garden bean instance during setup.

If you want to switch between different gardens while you work and ensure consistency between gardens and flower groups, the best choice would be a factory using an internal name map for flower classes. While the display itself may again be stored in the configuration, but you can create factory instances with various Garden instances at runtime.

+4
source

You can use the enumeration with the abstract factory method:

 public enum FlowerType{ ROSE("rose"){ public Rose createFlower(Garden g){ return new Rose(g); } }, OLEANDER("oleander"){ public Oleander createFlower(Garden g){ return new Oleander(g); } }; private final static Map<String, FlowerType> flowerTypes = new HashMap<String, FlowerType>(); static { for (FlowerType flowerType : values()){ flowerTypes.put(flowerType.getName(), flowerType); } private final String name; protected FlowerType(String name){ this.name = name; } public String getName(){ return name; } public abstract Flower createFlower(Garden g); public static FlowerType getFlower(String name){ return flowerTypes.get(name); } } 

I can’t say if this is the best way in your case, as I need some information.

+2
source

Besides using an enumeration or display, you can use reflection if there is a simple mapping of the name to the class.

 public Flower createFlower(final String name) { try { Class clazz = Class.forName("mypackage.flowers."+name); Constructor con = clazz.getConstructor(Garden.class); return (Flower) con.newInstance(g); } catch (many exceptions) { throw new cannot create flower exception. } } 
+1
source

I would suggest removing the state from your factory object and passing the Garden object as an argument in the static factory method:

 public class FlowerFactory { private FlowerFactory() {} public static Flower createFlower(final String name, Garden g) { Flower result = null; if (Rose.NAME.equals(name)) { result = new Rose(g); } else if (Oleander.NAME.equals(name)) { result = new Oleander(g); } else if ... { ... } ... return result; } 
0
source

You can also do this by storing line names on the map to avoid the if / elses series.

 Map<String, Class> map; map.get(name).newInstance(); 

If you have full control over your classes, you can instantiate using reflection directly from the string name, for example,

 Class.forName(name); 

In addition, you can also try the dependency framework. Some of them provide the ability to retrieve an instance of an object from a string name.

0
source

If all of your Flower have the same constructor signature, you can use reflection to set the parameter in the constructor.

Obviously, this falls into the realms of dependency injection, but maybe this is what you do :)

If you have many different parameters in your constructor, if it is safe, you can specify the type of each parameter to view the instance that should pass, something like what Guice does.

0
source

Source: https://habr.com/ru/post/1339052/


All Articles