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

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

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

Мне хотелось бы что-то вроде:

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

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

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

Ответ 1

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

  • Правильно обрабатывать сериализацию
  • Проверка нулевого значения обработки
  • Может быть, добавьте специализации для атомных типов, если вы беспокоитесь о служебных нагрузках на autoboxing.
  • ?? Я уверен, что есть больше gotchas

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

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

К сожалению, в наши дни это все равно похоже на программирование в сборке... Groovy, С# и т.д., и т.д., возможно, будет лучшим выбором, если у вас есть выбор.

Ответ 2

Отметьте мои аннотации 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, наблюдателей, делегатов, mixins и т.д.

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

Ответ 3

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

Ответ 4

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

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

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

www.springsource.org/download

www.hibernate.org/

Ответ 5

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

облегченный формат обмена данными

. Вот ссылка на JSON? Bean translator.

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

Попробуйте SEAM framework от JBoss, вам это понравится.

Ответ 9

Как только я попробовал это:

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;
    }
}

Ответ 10

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

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

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

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

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

Ответ 11

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

}