Использование деструктурирующего синтаксиса для объектов java - PullRequest
1 голос
/ 08 января 2020

Я пишу код, с помощью которого мне приходится обходить довольно сложные структуры данных, реализованные в виде 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. Для значимых графов объектов это может быть очень дорого.

Ответы [ 2 ]

1 голос
/ 13 января 2020

Я думаю, что этот вопрос больше о том, как превратить объекты в данные Clojure, чем конкретно о деструктурировании, т. Е. Если вы решите проблему преобразования, проблема с деструктурой уже решена.

Clojure 1.10 определяет datafy и Datafiable. Вы можете расширить протокол Datafiable для своих типов и иметь полный контроль над тем, как они представлены в виде данных. Вот простой пример использования java.net.URI:

(extend-protocol Datafiable
  java.net.URI
  (datafy [u]
    {:host (.getHost u)}))

(datafy (java.net.URI. "http://clojure.org"))
=> {:host "clojure.org"}

. Он также определяет nav и Navigable, которые вы можете реализовать для отложенной навигации, например, для обработки круговых структур или данных, которые дорогая реализация.

Я думаю, что некоторая комбинация bean -подобной функции (для общих случаев) с datafy и nav должна дать вам весь необходимый вам контроль / гибкость.

1 голос
/ 08 января 2020

Вы смотрели на clojure.core/bean?

(bean (java.util.Date.))

Это преобразует Java объект с геттерами и сеттерами в карту Clojure:

{:day 3,
 :date 8,
 :time 1578498930902,
 :month 0,
 :seconds 30,
 :year 120,
 :class java.util.Date,
 :timezoneOffset 0,
 :hours 15,
 :minutes 55}

Хотя он выиграл не делайте это рекурсивно, для этого вы можете использовать clojure.java.data. Это описано в этом вопросе: Как я могу использовать clojure.core / bean рекурсивно?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...