Как создать экземпляр набора различных объектов из списка json без регистра переключения?

#java #oop #design-patterns

Вопрос:

Я хочу создать экземпляр набора объектов из списка json.

Рассмотрим список json

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

Конечно, есть и другие поля, которые определяют каждый объект. Для каждого объекта я создал запись

 public record object1 (String area, ...) {}  public record object2 (String colour, ...) {}  public record object3 (String height, ...) {}  

В общем, я мог бы создать случай переключения, который проверяет класс свойств и выбирает правильный конструктор записей. Мне это кажется очень плохим стилем. В прошлом я использовал полиморфизм с подходящим суперклассом. и использовал перечисление, которое создало объект. В этом случае record класс имеет только объект как супер.

Я хочу придерживаться записи, так как я храню только простые данные внутри этого объекта.

Там я задавался вопросом, как с этим справиться. Как лучше всего справиться с чем-то подобным? Какой шаблон можно использовать для решения этой проблемы?

Комментарии:

1. Вы можете попробовать использовать библиотеку, которая уже поддерживает это, например, Джексона (хотя «класс», возможно, должен быть FQCN). Если вы хотите сделать это самостоятельно, попробуйте мета-фабрику/хранилище фабрики, например 1) вы вызываете мета-фабрику со значением «класс», чтобы получить фактическую фабрику объектов, и 2) вы передаете данные записи на эту фабрику, чтобы она создала фактический экземпляр.

2. Спасибо за подсказку. Возможно, мне придется сделать это самому, так как в будущем у меня может быть база данных, поэтому мне, возможно, придется настроить ее самостоятельно. У вас есть пример этого шаблона репозитория мета-фабрики/фабрики, который я мог бы просмотреть?

3. Я добавлю один в ответ, чтобы дать вам начало

Ответ №1:

Я немного расширю свой комментарий, чтобы показать примерный пример того, как может выглядеть подход «мета-фабрики».

Во-первых, вы определяете интерфейс для реальных фабрик объектов. Я оставлю для вас реализации, они довольно прямолинейны (точно так же, как это делали бы ваши дела в блоке коммутаторов).

 interface Factorylt;Tgt; {   //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); }  

Затем создайте репозиторий. Это также всего лишь приблизительный набросок:

 class FactoryRepo {  //Mapping between name and type, this could also be maintained by the factories or some other piece of code  Maplt;String, Classlt;?gt;gt; typeMapping = ...;   //The actual repository, a simple map for starters  Maplt;Classlt;?gt;, Factorylt;?gt;gt; repository = ...;   //register a new factory, I'll add the name mapping too but you could move this into a separate method  public lt;Tgt; void registerFactory(String name, Classlt;Tgt; type, Factorylt;Tgt; factory) {  typeMapping.put(name, type);  repository.put(type, factory);  }   //lookup factory by class  @SuppressWarnings({ "unchecked" })  public lt;Tgt; Factorylt;Tgt; lookupFactory(Classlt;Tgt; type) {  //unfortunately this cast is necessary  return (Factorylt;Tgt;)repository.get(type);  }    //lookup factory by name  public Factorylt;?gt; lookupFactory(String name) {  return lookupFactory(typeMapping.get(name));  } }  

Конечно, вам нужно зарегистрировать заводы. Это можно сделать вручную или с помощью фреймворков, которые обнаруживают реализации (например, Spring, CDI или даже SPI Java). Ручной подход может выглядеть следующим образом:

 FactoryRepo repo = ...; //get if from somewhere   repo.registerFactory("object1", Object1Type.class, new Object1Factory());  repo.registerFactory("object2",ColoredObject.class, record -gt; {  //code to build an instance of ColoredObject from record and return 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  Factorylt;?gt; 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);  

Комментарии:

1. Круто, это хорошая отправная точка. Есть некоторые более мелкие детали, которые я упускаю. или хотел бы уточнить. Я предполагаю Object1Factory , что интерфейс реализован Factory правильно? Существует много движущихся частей, у вас есть ссылка для этой конкретной реализации. Я искал заводской шаблон, но примеры довольно просты с абстрактными классами. В любом случае я благодарю вас за замечательные подсказки.

2. Один из последующих вопросов будет заключаться в том, как создать экземпляр Maplt;String, Classlt;?gt;gt; typeMapping и repository ? Должен ли я использовать a new HashMaplt;gt;() и вводить записи?

3. @A. Дюма нет, я довольно часто использовал этот шаблон, но у меня нет никакой ссылки, чтобы поделиться. Это, вероятно, лучше, чтобы поиграть с ним немного и разделить подход на отдельные части: 1) реализации Factory интерфейса (и да, Object1Factory это одна из них), 2) как инициализировать репозиторий — старт с ручным регистрации, и затем работать ваш путь, чтобы во время выполнения обнаружения и 3) репозиторий себя, так как может потребоваться дополнительная проверка и синхронизации, в зависимости от ваших потребностей. Для начала, карты в репозитории могут быть простыми HashMap или ConcurrentMap экземплярами.

4. Ты просто потрясающая. Я только что протестировал его, и он работает как заклинание. Мне все еще нужно привести в порядок чистый код и внести некоторые коррективы в инкапсуляцию, но это был просто взрыв. Я предполагаю, что должна быть одна управляющая логика, которая принимает правильный repo.lookupFactory , если я хотел правильный объект. но это потрясающе. Я должен сказать, что это должно быть где-то в блоге, так как это просто здорово

5. @A. Дюма, добро пожаловать 🙂 — управляющая логика будет в основном перебирать массив json, проверять каждый из объектов, вытаскивать поле «класс» и, наконец, искать фабрику.