Альтернативы JavaBeans?

Я ненавижу паттерн JavaBeans страстью, пылающей, как огонь тысячи солнц. Почему?

  • Подробный. Сейчас 2009 год. Мне не нужно писать 7 LOC для собственности. Если у них есть слушатели событий, держитесь за свою шляпу.
  • Нет типобезопасных ссылок. Нет безопасного для типов способа ссылки на свойство. Вся суть Java в том, что она типобезопасна, а ее самый популярный шаблон вовсе не типобезопасен.

Я бы хотел что-то вроде:

class Customer {
    public Property<String> name = new Property();
}

Я в основном веб-разработчик, поэтому ему нужна поддержка JPA и Wicket.

Помогите мне сойти с джавабийского поезда!

Что вы имеете в виду под «Они поощряют ссылки на строки»?

cletus 14.01.2009 02:06

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

jsight 14.01.2009 02:15

Не в поезде JavaBean? Шагните по этому пути к платформе C# ...

duffymo 14.01.2009 03:36

Поскольку вы упомянули Wicket, я вспомнил, что это: wicketwebbeans.sourceforge.net Может быть, это могло бы вам помочь?

Esko 14.01.2009 10:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
11
4
3 729
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Вы можете попробовать Groovy - динамически типизированный, основанный на JVM (и полностью совместимый с Java) язык с «реальными» свойствами.

Вы также можете создать генератор кода, который создает ваши классы .java из написанного вами DSL. У вас может быть какая-то разметка, описывающая имя вашего класса, необходимые свойства и их типы. Затем обработайте этот файл программой, которая генерирует ваши javabeans. Или вы можете использовать аннотацию и постобработать файлы классов, используя что-то вроде ASM, чтобы внедрить аксессоры и мутаторы. Я также считаю, что Spring предоставляет некоторые из этих функций, но я ими не пользовался.

Определенно подход, который сработает, но я думаю, что он упустил суть сообщения: он считает JavaBeans слишком многословным и сложным. Разве добавление еще одного слоя через DSL или аннотации и постобработка не усугубляет ситуацию?

Ian Varley 14.01.2009 02:30

@ian: DSL может быть неплохой идеей, потому что использование DSL, как правило, делает вещи лаконичными и лаконичными, что в точности соответствует целям плаката.

Chii 14.01.2009 13:15

@ian: В краткосрочной перспективе я бы согласился из-за усилий, необходимых для создания постпроцессора. Однако, как только это будет сделано, код, который его использует, будет проще в запрошенном виде.

Arcane 14.01.2009 18:29

Для Интернета я бы предложил JSON (нотация объектов JavaScript),

a lightweight data-interchange format

. Вот ссылка на JSON? Bean переводчик.

Ответ принят как подходящий

Я думаю, вы очень близки с декларацией, которая у вас там (см. Рисунок ниже). Однако при использовании подхода, отличного от bean-компонентов, вы, вероятно, потеряете поддержку, предоставляемую большинством инструментов, предполагающих, что действует протокол JavaBeans. Пожалуйста, будьте добры. Код ниже не у меня в голове ...

public class Property<T> {
    public final String name;
    T value;
    private final PropertyChangeSupport support;

    public static <T> Property<T> newInstance(String name, T value, 
                                              PropertyChangeSupport support) {
        return new Property<T>(name, value, support);
    }

    public static <T> Property<T> newInstance(String name, T value) {
        return newInstance(name, value, null);
    }

    public Property(String name, T value, PropertyChangeSupport support) {
        this.name = name;
        this.value = value;
        this.support = support;
    }

    public T getValue() { return value; }

    public void setValue(T value) {
        T old = this.value;
        this.value = value;
        if (support != null)
            support.firePropertyChange(name, old, this.value);
    }

    public String toString() { return value.toString(); }
}

а затем продолжайте и используйте его:

public class Customer {
    private final PropertyChangeSupport support = new PropertyChangeSupport();

    public final Property<String> name = Property.newInstance("name", "", support);
    public final Property<Integer> age = Property.newInstance("age", 0, support);

    ... declare add/remove listenener ...
}


Customer c = new Customer();
c.name.setValue("Hyrum");
c.age.setValue(49);
System.out.println("%s : %s", c.name, c.age);

Итак, теперь объявление свойства - это одна строка кода, и включена поддержка изменения свойства. Я назвал методы setValue () и getValue (), чтобы они по-прежнему выглядели как bean-компонент для кодирования, такого как Rhino и тому подобное, но для краткости вы можете добавить просто get () и set (). Остальное оставим читателю в качестве упражнения:

  • Правильная обработка сериализации
  • Обработка проверки нулевого значения
  • Возможно, добавьте специализации для атомарных типов, если вам небезразличны накладные расходы на автобоксинг.
  • ?? Я уверен, что есть еще подводные камни

Также обратите внимание, что вы можете создать подкласс (обычно как анонимный класс) и переопределить setValue (), чтобы обеспечить дополнительную проверку параметров.

Я не думаю, что вам действительно удастся уйти от «ссылок на строки», поскольку именно в этом и заключается суть отражения.

К сожалению, в наши дни это все еще похоже на программирование на ассемблере ... Groovy, C# и т. д. И т. Д. Могут быть лучшим выбором, если у вас есть выбор.

Попробуйте фреймворк Шов от JBoss, он вам должен понравиться.

Однажды я попробовал это:

interface IListenable {
    void addPropertyChangeListener( PropertyChangeListener listener );
    void removePropertyChangeListener( PropertyChangeListener listener );
}

abstract class MyBean extends IListenable {
    public abstract void setName(String name);
    public abstract String getName();

    // more things
}

public class JavaBeanFactory {

   public <T> Class<T> generate(Class<T> clazz) {
      // I used here CGLIB to generate dynamically a class that implements the methods:
      // getters
      // setters
      // addPropertyChangeListener
      // removePropertyChangeListener
   }
}

Я использовал это как это (это просто пример):

public class Foo {
    @Inject
    public Provider<MyBean> myBeanProvider;

    public MyBean createHook(MyBean a) {
        final MyBean b  = myBeanProvider.get();
        a.addPropertyChangeListener(new PropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent evt) {
                 b.setName((String) evt.getNewValue());
             }
        });
        return b;
    }
}

Используйте Spring Framework. Его цель - упростить разработку на Java за счет абстрагирования многих оснований, на которые вы жалуетесь.

Вы можете использовать его с Hibernate, что упростит вам взаимодействие с источниками данных.

Полезные сайты:

www.springsource.org/download

www.hibernate.org/

Ознакомьтесь с моими аннотациями Bean на

http://code.google.com/p/javadude/wiki/Annotations

В основном вы делаете такие вещи, как:

@Bean(
  properties = {
    @Property(name = "name"),
    @Property(name = "phone", bound=true),
    @Property(name = "friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

вместо того, чтобы самостоятельно определять все эти дополнительные методы get / set и т. д.

Есть и другие атрибуты для определения equals / hashCode, наблюдателей, делегатов, миксинов и т. д.

Это набор аннотаций и обработчик аннотаций, который работает в eclipse или в сборке из командной строки (например, в ant). Процессор создает суперкласс, содержащий весь сгенерированный код (процессоры аннотаций не могут изменить класс, содержащий аннотации, кстати)

Когда я впервые использовал C#, мне понравились свойства, но теперь, спустя некоторое время, используя их с VS 2008, я должен сказать, что предпочитаю set- / get-методы.

Главное - это мой личный стиль работы. Когда у меня есть новый класс и я хотел бы знать, что я могу с ним сделать, я просто набираю classname.set, и Eclipse показывает мне, какие «Свойства» я могу изменить. То же самое и с получением. Возможно, это просто плохой способ VS, но там мне нужно пролистать длинный список этого itelisense (где все смешано, вместо того, чтобы сначала показывать свойства), чтобы узнать после компиляции, что свойство, которое я хотел установить, доступно только для чтения. ..дох!

Да, в Java вам нужно много строк, но я просто пишу свои атрибуты и говорю IDE: «Пожалуйста, создайте для меня геттеры и сеттеры». Но эти методы, как правило, занимают много места, поэтому я также хотел бы иметь области в Java, которые я мог бы складывать в IDE.

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

К сожалению, у меня нет решения второй проблемы (возможно, кроме определения констант, содержащих имена свойств).

Что я делаю для создания шаблона слушателя, так это то, что объекты моей модели расширяются от суперкласса AbstractJavaBean, который обрабатывает его с помощью отражения. Затем я могу использовать способ создания геттеров / сеттеров по умолчанию, за исключением того, что сеттеры нужно переписать следующим образом:

public void setRemarks(String remarks) {
    set("remarks", remarks);
}

Затем AbstractJavaBean.set использует отражение (через beanutils Apache commons) для чтения старого значения свойства "комментарии" через его геттер, устанавливает новое значение в поле с именем "комментарии" и запускает событие изменения свойства, используя старое и новое значения. . Фактически, эта идея может быть расширена, чтобы позволить зависимым «производным» свойствам автоматически запускать изменения свойств, когда одно из свойств, основанное на изменениях, таких как «возраст», изменяется при изменении свойства «BirthDate». Вся эта логика может быть закодирована в одном месте внутри AbstractJavaBean и использоваться любым объектом модели.

Я искал то же самое и был очень удивлен, что не смог найти подходящую библиотеку. Поскольку я регулярно чувствовал боль, 2 года назад я начал небольшой проект по решению этих проблем:

Он доступен по адресу: https://github.com/aditosoftware/propertly. Сейчас мы используем его в нашем продукте.

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

Пример использования:

Простой IPropertyPitProvider. Свойства - имя, фамилия и возраст.

// Generics describe parent, self and children
public class StudentPropertyPitProvider 
    extends AbstractPPP<IPropertyPitProvider, StudentPropertyPitProvider, Object>
{
  // IPropertyDescription gives static access to an IProperty's meta data like name and type.
  public static final IPropertyDescription<StudentPropertyPitProvider, String> FIRST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, String> LAST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, Integer> AGE =
      PD.create(StudentPropertyPitProvider.class);


  // Getters and setters can of course still be used for easier access. 
  public String getFirstName()
  {
    // getValue and setValue is available at AbstractPPP. That class is used for easier access. 
    // Propertly can be used without inheriting from that class, too.
    return getValue(FIRST_NAME);
  }

  public void setFirstName(String pFirstName)
  {
    setValue(FIRST_NAME, pFirstName);
  }

  public String getLastName()
  {
    return getValue(LAST_NAME);
  }

  public void setLastName(String pLastName)
  {
    setValue(LAST_NAME, pLastName);
  }

  public Integer getAge()
  {
    return getValue(AGE);
  }

  public void setAge(Integer pAge)
  {
    setValue(AGE, pAge);
  }

}

Используя определенного провайдера:

public class Sample
{

  public static void main(String[] args)
  {
    // Hierarchy is necessary to initialize the IPropertyPitProviders and for advanced features.
    Hierarchy<StudentPropertyPitProvider> hierarchy =
        new Hierarchy<>("student1", new StudentPropertyPitProvider());
    // The created student can be accessed from the hierarchy.
    StudentPropertyPitProvider student = hierarchy.getValue();
    // Listeners can be added.
    student.addPropertyEventListener(new PropertyPitEventAdapter()
    {
      @Override
      public void propertyChanged(IProperty pProperty, Object pOldValue, Object pNewValue)
      {
        System.out.println(pProperty.getName() + " = " + pNewValue);
      }
    });

    // The following calls will cause
    //  FIRST_NAME=Nils
    //  LAST_NAME=Holgersson
    //  AGE=32
    // to be printed on console through the listener.
    student.setFirstName("Nils");
    student.setLastName("Holgersson");
    student.setAge(32);
  }

}

Другие вопросы по теме