简体   繁体   中英

How to instantiate a set of different object from json list without switch case?

I want to instantiate a set of object from a json list.

Consider the json list

[
   {
...
      "class":"object1",
      "area":45
...
   },
   {
...
      "class":"object2",
      "colour":"red"
   },
   {
...
      "class":"object3",
      "height":90
...
   }
]

Surely there are there fields that define each object. For each object I created a record

public record object1 (String area, ...) {}

public record object2 (String colour, ...) {}

public record object3 (String height, ...) {}

In general I could create a switch case that checks the property class and the chooses the correct record constructor. This seems to me very bad style. In the past I used polymorphism with a fitting super class. and used an enum that created the object. In this case the record class only has object as super.

I want to stick with record since its I am only holding simple data inside this object.

There I was wondering how to handle this. What is the best practice to handle something like this? What pattern can be used to solve this?

I'll expand a little on my comment to show a rough example of what the "meta factory" approach could look like.

First, you define an interface for the actual object factories. I'll leave the implementations for you, those are pretty straight forward (just like what your cases in the switch block would have been doing).

interface Factory<T> { 
  //I'll assume you're using a JSON library here which provided a JsonObject class
  //Change the parameter as required to reflect your actual code
  T createInstance(JsonObject record);
}

Then create a repository. This also is just a rough outline:

class FactoryRepo {
  //Mapping between name and type, this could also be maintained by the factories or some other piece of code
  Map<String, Class<?>> typeMapping = ...;

  //The actual repository, a simple map for starters
  Map<Class<?>, Factory<?>> repository = ...;

  //register a new factory, I'll add the name mapping too but you could move this into a separate method
  public <T> void registerFactory(String name, Class<T> type, Factory<T> factory) {
    typeMapping.put(name, type);
    repository.put(type, factory);
  }

  //lookup factory by class
  @SuppressWarnings({ "unchecked" })
  public <T> Factory<T> lookupFactory(Class<T> type) {
    //unfortunately this cast is necessary
    return (Factory<T>)repository.get(type);
  }
      
  //lookup factory by name
  public Factory<?> lookupFactory(String name) {
    return lookupFactory(typeMapping.get(name));
  }
}

Of course, you need to register the factories. This can be done manually or by using frameworks that detect implementations (eg Spring, CDI or even Java's SPI). A manual approach could look like this:

 FactoryRepo repo = ...; //get if from somewhere

 repo.registerFactory("object1", Object1Type.class, new Object1Factory());
 repo.registerFactory("object2",ColoredObject.class, record -> {
   //code to build an instance of ColoredObject from record and return it
 });

Finally, you use it:

 FactoryRepo repo = ...; //get if from somewhere

 JsonObject record = ...; //assumes you have it handy

 //Unfortunately, you only get "Object" since the record's class can be anything
 //If you have an interfacy etc. you could limit the boundary to this
 Factory<?> factory = repo.lookupFactory(record.get("class"));
 Object instance = factory.createInstance(record);


 //If you know the type, you can try like this:
 ColoredObject co = repo.lookupFactory(ColoredObject.class).createInstance(record);

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