Я пишу код, с помощью которого мне приходится обходить довольно сложные структуры данных, реализованные в виде Java классов, в основном POJO. Мне нравится, как я могу использовать многоуровневый синтаксис деструктуризации при работе с картами или записями clojure - я думаю, что это делает код более понятным, чем навигацию по java объектным графам. или -> операции.
Например, я хотел бы иметь возможность написать следующий код (составленный):
(let [{registration-date :registrationDate ; immediate field access
{first-name :firstName ; destructuring nested object
last-name :lastName
email: email} :personal-data
[first-order & remaining-orders] :order-history ; destructuring nested collection
} account]
(... do something directly with symbols first-name, email, first-order, etc...))
То, как я это делаю сейчас, - это у меня есть набор функций 'bridge', которые конвертируют Java объект данного типа в карту. Например, это может выглядеть следующим образом:
(defn map-account [account]
{:registration-date (.getRegistrationDate account)
:order-history (map bean (.getOrderHistory account))
:personal-data (bean (.getPersonalData account)})
Это работает, но есть много склонных к ошибкам (например, что если account.getPersonalData () возвращает ноль), и мне интересно, есть ли способ использовать синтаксис, такой как деструктурирование карты для Java объектов, которые более или менее «из коробки»?
Подумав об этом, я не ценю, если это фактическая деструктура или что-то похожее это (например, какой-то дурацкий макрос), если оно дает мне выразительность, сравнимую с предыдущей. Дополнительный бонус, если это можно сделать ленивым образом, например, ветви графа объектов вообще не просматриваются, если к ним нет фактического доступа.
Редактировать: Можно достичь чего-то подобного это с clojure.core.bean
или clojure.java.data
, где последний является рекурсивным, а первый - нет. Я прошу признать, что они могут сыграть свою роль, и на самом деле я использую bean в приведенном выше примере.
Моя главная проблема при использовании этих подходов заключается в том, что они негибкие и не допускают никаких изменений результат. Чтобы привести несколько примеров, где это может оказаться очень полезным:
- Один из объектов может быть частично несовместим с соглашением JavaBeans
- Может возникнуть необходимость сгладить некоторые части иерархия, особенно когда базовый Java API очень идиоматичен c. В некоторых API регулярно используется служебные классы для пар объектов, которые обычно собираются вместе, например, StateAndRef . В Clojure это только усложняет код
- Существует ограниченный контроль над тем, как далеко должна рекурсия go. Для значимых графов объектов это может быть очень дорого.