#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
? Должен ли я использовать anew HashMaplt;gt;()
и вводить записи?3. @A. Дюма нет, я довольно часто использовал этот шаблон, но у меня нет никакой ссылки, чтобы поделиться. Это, вероятно, лучше, чтобы поиграть с ним немного и разделить подход на отдельные части: 1) реализации
Factory
интерфейса (и да,Object1Factory
это одна из них), 2) как инициализировать репозиторий — старт с ручным регистрации, и затем работать ваш путь, чтобы во время выполнения обнаружения и 3) репозиторий себя, так как может потребоваться дополнительная проверка и синхронизации, в зависимости от ваших потребностей. Для начала, карты в репозитории могут быть простымиHashMap
илиConcurrentMap
экземплярами.4. Ты просто потрясающая. Я только что протестировал его, и он работает как заклинание. Мне все еще нужно привести в порядок чистый код и внести некоторые коррективы в инкапсуляцию, но это был просто взрыв. Я предполагаю, что должна быть одна управляющая логика, которая принимает правильный
repo.lookupFactory
, если я хотел правильный объект. но это потрясающе. Я должен сказать, что это должно быть где-то в блоге, так как это просто здорово5. @A. Дюма, добро пожаловать 🙂 — управляющая логика будет в основном перебирать массив json, проверять каждый из объектов, вытаскивать поле «класс» и, наконец, искать фабрику.