Java классы с динамическими полями - PullRequest
6 голосов
/ 27 июня 2010

Я ищу умные способы создания динамических классов Java, то есть классов, где вы можете добавлять / удалять поля во время выполнения. Сценарий использования: у меня есть редактор, в котором пользователи должны иметь возможность добавлять поля к модели во время выполнения или, возможно, даже создавать всю модель во время выполнения.

Некоторые цели дизайна:

  • Введите safe без приведений, если это возможно, для пользовательского кода, который работает с динамическими полями (этот код будет получен из плагинов, которые расширяют модель непредвиденными способами).
  • Хорошая производительность (вы можете побить HashMap? Может быть, использовать массив и назначать индексы для полей во время установки?)
  • Поле «повторное использование» (т. Е. Если вы используете поле одного и того же типа в нескольких местах, должно быть возможно определить его один раз, а затем повторно использовать).
  • Вычисляемые поля, которые зависят от значения других полей
  • Сигналы следует отправлять при изменении значения полей (необязательно через API-интерфейс Beans)
  • «Автоматически» родительские дочерние отношения (когда вы добавляете дочерний элемент к родительскому элементу, указатель родительского элемента в дочернем элементе должен быть установлен как «свободный»).
  • Легко понять
  • Простота в использовании

Обратите внимание, что это вопрос "мыслить вне круга". Я приведу пример ниже, чтобы у вас было настроение: -)

Ответы [ 5 ]

2 голосов
/ 27 июня 2010

Введите safe без приведений, если это возможно, для пользовательского кода, который работает с динамическими полями (этот код будет получен из плагинов, которые расширяют модель непредвиденными способами)

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

Лучшее, что вы можете сделать - это иметь интерфейс с кучей методов, таких как String getStringValue(String field), int getIntValue(String field) и так далее. И, конечно, вы можете сделать это только для заранее определенного набора типов. Любое поле, тип которого не входит в этот набор, потребует приведения типа.

2 голосов
/ 27 июня 2010

Очевидный ответ - использовать HashMap (или LinkedHashMap, если вы заботитесь о порядке полей). Затем вы можете добавить динамические поля с помощью get(String name) и set(String name, Object value) метода.

Этот код может быть реализован в общем базовом классе. Поскольку существует всего несколько методов, также легко использовать делегирование, если вам нужно расширить что-то еще.

Чтобы избежать проблемы приведения, вы можете использовать типобезопасную карту объектов :

    TypedMap map = new TypedMap();

    String expected = "Hallo";
    map.set( KEY1, expected );
    String value = map.get( KEY1 ); // Look Ma, no cast!
    assertEquals( expected, value );

    List<String> list = new ArrayList<String> ();
    map.set( KEY2, list );
    List<String> valueList = map.get( KEY2 ); // Even with generics
    assertEquals( list, valueList );

Хитрость здесь в том, что ключ содержит информацию о типе:

TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );

Спектакль будет в порядке.

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

Вычисляемые поля могут быть реализованы со второй картой, которая хранит Future экземпляров, которые выполняют вычисления.

Поскольку все манипуляции выполняются всего двумя (или, по крайней мере, несколькими) методами, отправка сигналов проста и может быть выполнена любым удобным вам способом.

Чтобы реализовать автоматическую обработку родительских / дочерних элементов, установите прослушиватель сигнала для сигнала «set parent» дочернего элемента, а затем добавьте дочерний элемент к новому родительскому элементу (и при необходимости удалите его из старого).

Поскольку никакой фреймворк не используется и никаких хитростей не требуется, полученный код должен быть довольно чистым и простым для понимания. Отсутствие использования String в качестве ключей дает дополнительное преимущество, заключающееся в том, что люди не засоряют код строковыми литералами.

1 голос
/ 28 марта 2011

Я делаю почти то же самое, это чисто решение Java:

  1. Пользователи генерируют свои собственные модели, которые хранятся в виде схемы JAXB.
  2. Схема компилируется в классах Java налетать и храниться в пользовательских jars
  3. Все классы вынуждены расширять один «корневой» класс, в который можно поместить все дополнительные функции, которые вы хотите.
  4. Соответствующие загрузчики классов реализованы с помощью слушателей «изменения модели».

Говоря о производительности (что важно в моем случае), вы вряд ли сможете обойти это решение.Возможность повторного использования аналогична XML-документу.

1 голос
/ 28 марта 2011

Вы можете использовать библиотеки манипулирования байт-кодом для него. Недостатком этого подхода является необходимость создания собственного загрузчика классов для динамической загрузки изменений в классах.

1 голос
/ 27 июня 2010

То есть вы пытаетесь создать объектную модель нового типа с более динамическими свойствами, немного похожую на динамический язык?

Возможно, стоит взглянуть на исходный код Rhino (т. Е. Javascript, реализованный на Java), который сталкивается с аналогичной проблемой реализации системы динамических типов в Java.

Вдобавок ко всему, я подозреваю, что вы обнаружите, что внутренние HashMaps в конечном итоге работают лучше всего для ваших целей.

Я написал небольшую игру ( Тиран - доступный источник GPL ), использующий аналогичную динамическую объектную модель с использованием HashMaps, она работала великолепно, и производительность не была проблемой. Я использовал несколько приемов в методах get и set, чтобы разрешить динамические модификаторы свойств, я уверен, что вы могли бы сделать то же самое для реализации ваших сигналов, отношений родитель / потомок и т. Д.

[РЕДАКТИРОВАТЬ] См. источник BaseObject , как он реализован.

...