2014-10-16 2 views
2

Я нахожу, что делаю много классов для использования в здании GUI (следовательно, они должны соответствовать шаблону JavaBean). Это создало некоторые проблемы для меня в отношении инициализации. У меня часто есть какой-то метод, который требует довольно много времени, который должен выполняться после того, как состояние установлено.Инициализация JavaBean

Один из подходов состоит в том, чтобы документировать, что метод init() должен выполняться и надеяться, что люди его прочитают и уважают, но это неудобно и означает, что GUIBuilder не может использоваться только по назначению, а скорее дополнительный код имеет быть добавленным.

Я проверил «Эффективную Java» Блоха, эти форумы и, конечно же, я спросил доктора Google, но я ничего не придумал. Справедливости ради, это немного странный набор поисковых терминов.

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

public class BeanTest { 

    private int someValue;   // Just some number 
    private float anotherValue;  // Just another number 
    private double calculatedValue; // Calculated by some expensive process 

    private boolean isInitialised = false; // Is calculatedValue valid? 

    /** 
    * Default constructor made available for JavaBean pattern 
    */ 
    public BeanTest() { 
     someValue = 0; 
     anotherValue = 0; 
    } 

    //******* Getters and setters follow ************/ 
    public int getSomeValue() { 
     return someValue; 
    } 

    public void setSomeValue(int someValue) { 
     if (someValue == this.someValue) { 
      return; 
     }   
     isInitialised = false;  // Calculated value is now invalid 
     this.someValue = someValue; 
    } 

    public float getAnotherValue() { 
     return anotherValue; 
    } 

    public void setAnotherValue(float anotherValue) { 
     if (anotherValue == this.anotherValue) { 
      return; 
     } 
     isInitialised = false;  // Calculated value is now invalid 
     this.anotherValue = anotherValue; 
    } 

    /** 
    * This is where the time expensive stuff is done. 
    */ 
    public void init() { 
     if (isInitialised) { 
      return; 
     } 

     /* In reality this is some very costly process that I don't want to run often, 
     * probably run in another thread */ 
     calculatedValue = someValue * anotherValue; 

     isInitialised = true; 
    } 

    /** 
    * Only valid if initialised 
    */ 
    public double getCalculatedValue() { 

     init(); 

     return calculatedValue; 
    } 

    /** 
    * Code for testing 
    */ 
    public static void main(String[] args) { 
     BeanTest myBean = new BeanTest(); 
     myBean.setSomeValue(3); 
     myBean.setAnotherValue(2); 
     System.out.println("Calculated value: " + myBean.getCalculatedValue()); 
    } 
} 

Этот подход имеет несколько проблем. Например, он не распространяется хорошо (и некоторые из них действительно предназначены для расширения). Кроме того, я показываю только простой случай с тремя переменными; у реальных классов есть еще много. Вещи становятся беспорядком.

Может ли кто-нибудь предложить другой метод или шаблон, который мог бы помочь мне сохранить код более изящным и читаемым и все-таки позволить работать, как ожидалось, в GUI-компоновщике, пожалуйста?

P.S. Это должно быть изменчивым.


EDITED

Думаю, банальность я спрятался точки немного.

Фокус в том, что я хочу запускать init() только один раз, и только когда все установлено. Если бы я использовал шаблон построителя, это было бы легко, поскольку я бы поместил его в метод build(), но это элемент GUI, а также в шаблоне JavaBean.

Код, который у меня выше, представляет собой тривиализованную версию «шаблона», который я использую. Шаблон действительно работает, но есть много недостатков, как я заметил, особенно с расширяемостью (это слово?) И по мере роста числа переменных. Тривиальный пример выглядит хорошо, но реальный код начинает выглядеть ужасно.

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

+0

Поскольку класс является изменчивым - любые ранее рассчитанные результаты недействительны немедленно, когда свойство изменяется, или вы должны установить все из них еще раз, прежде чем вычисление может быть выполнено снова? Другими словами, всегда ли вы устанавливаете все свойства при обновлении компонента? – Vegard

ответ

1

Наивный подход: почему бы не просто вызвать init() в сеттере вместо этого?

Немного больше фантазии: используйте объект PropertyChangeSupport. Пример использования:

import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.beans.PropertyChangeSupport; 

public class TestBean implements PropertyChangeListener{ 
    private int someValue; 
    private PropertyChangeSupport changeSupport; 

    public TestBean() { 
     changeSupport = new PropertyChangeSupport(this); 
     changeSupport.addPropertyChangeListener(this); 
    } 

    private void init() { 
     //do something time consuming, maybe even on a different thread, using Futures? 
    } 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     init(); 
    } 

    public int getSomeValue() { 
     return someValue; 
    } 

    public void setSomeValue(int someValue) { 
     int oldValue = this.someValue; 
     this.someValue = someValue; 
     changeSupport.firePropertyChange("someValue", oldValue, someValue); 
    } 
} 
+0

Спасибо за ответ. К сожалению, это приводит к большему количеству вызовов init(), чем это строго необходимо. Расчеты занимают много времени, поэтому это нежелательно. – timbo

+1

Вы можете отслеживать, есть ли у вас 'init'ed уже и только вызывать его один раз? –

+0

Это графический интерфейс Swing? Похоже, вы можете столкнуться с ситуациями, когда Thread Dispatch Thread может блокировать ожидание завершения вычислений с использованием вычисления на основе getter-подхода, оставляя вас с невосприимчивым приложением. – Vegard

0

Я думаю, что я должен признать, что то, чего я пытаюсь достичь, не может быть сделано. Вот цитата из Блоха «Effective Java»:

К сожалению, модель JavaBeans имеет серьезные недостатки его самостоятельно.Поскольку построение разделено на несколько вызовов, JavaBean может быть в несогласованном состоянии частично через его конструкцию. Класс не имеет возможности обеспечить согласованность только , проверяя правильность параметров конструктора.

Хотя он не совсем точно отвечает на мой вопрос, я думаю, что любой ответ, который обходит переменную isInitialized, столкнулся с проблемой, которую описывает Блох.

Смежные вопросы