В реальном случае у меня есть около 58 редактируемых полей, смешанных из текста и чисел. И вдвое меньше вычисляемых полей.
Повозившись, я нашел много решений. Моя проблема в том, что у меня нет опыта, чтобы решить или судить, какой путь лучше всего выбрать, если вообще есть. Двумя основными кандидатами являются:
- Первый вариант — просто добавить прослушиватели документов во все редактируемые поля пользовательского интерфейса. При изменении они обновляют данные в модели и вызывают метод для обновления всех полей в пользовательском интерфейсе. Недостаток - по моему грубому мнению - в том, что у меня более 50 полей. Я не знаю, как его закодировать, не написав отдельный прослушиватель для каждого компонента пользовательского интерфейса. Это также может затруднить последующую обработку изменений в коде.
- Создайте массив класса, который регистрирует каждый редактируемый или вычисляемый компонент пользовательского интерфейса. Класс зарегистрирует не только компонент пользовательского интерфейса, но и, используя отражение, метод, который будет вызываться для установки или получения информации из объекта модели. Список документов по-прежнему будет обрабатывать изменения, но теперь он может быть одинаковым для всех компонентов пользовательского интерфейса, поскольку массив может обрабатывать изменения. Хорошим моментом является то, что весь перевод между моделью и пользовательским интерфейсом может быть закодирован в одном классе. Недостаток — размышления, люди всегда советуют избегать этого.
Код, который я использую для тестирования:
public class Comunication {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
//Main Window
JFrame frame = new JFrame();
frame.setTitle("SumTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(500,200));
frame.setVisible(true);
//Main Panel
JPanel pane = new JPanel();
frame.setContentPane(pane);
//Component
JTextField valueA = new JTextField("VALUE A");
JTextField valueB = new JTextField("VALUE B");
JTextField valueSum = new JTextField("VALUE SUM");
pane.add(valueA);
pane.add(valueB);
pane.add(valueSum);
}
});
}
}
class Data {
private int a;
private int b;
private int sum;
public Data() {
a = 1;
b = 2;
Calculate();
}
public void Calculate() {
sum = a + b;
}
public int getA() { return a; }
public int getB() { return b; }
public int getSUM() { return sum; }
public void setA(int i) { a = i; }
public void setB(int i) { b = i; }
}
Часть 2:
Экспериментируя с информацией, предоставленной пользователями, я взял на себя смелость попробовать что-то еще.
Одним из решений было бы создание класса прослушивателя, связывающего представление и модель. Этот прослушиватель следует немного менять каждый раз, когда он добавляется в поле (представление). Это единственный найденный способ связать поле с методом в модели без использования отражения.
Итак, алгоритм обновлений таков: при изменении представление обновляет модель.
После этого: глобальный контроллер обновляет все представления новой информацией о модели.
Проблемы, которые я обнаружил, вероятно, из-за отсутствия опыт:
- Поскольку все представления имеют прослушиватели изменений документа: когда глобальный контроллер обновляет все представления/поля, они снова вызывают прослушиватель. Обходной путь, который я нашел, заключался в добавлении «тихого» флага к прослушивателю.
- Когда представление обновляет модель и вызывает глобальный контроллер для обновления всех других представлений, оно не может обновиться само. Я пока не нашел причину, думаю, может быть, это вызывает петлю. Обходной путь заключался в том, чтобы сообщить глобальному контроллеру, который его вызвал, и обновить все представления, кроме вызывающего.
Мнения будут очень приветствоваться, я хочу сделать это правильно или лучше.
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Comunication {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
//Main Window
JFrame frame = new JFrame();
frame.setTitle("SumTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(500,200));
frame.setVisible(true);
//Main Panel
JPanel pane = new JPanel();
frame.setContentPane(pane);
//Data Model
DataModel model = new DataModel();
GlobalUpdateController viewUpdateController = new GlobalUpdateController();
//Component
JTextField valueA = new JTextField("");
JTextField valueB = new JTextField("");
JTextField valueSum = new JTextField("");
valueA.setPreferredSize(new Dimension(30, 20));
valueB.setPreferredSize(new Dimension(30, 20));
valueSum.setPreferredSize(new Dimension(30, 20));
pane.add(valueA);
pane.add(valueB);
pane.add(valueSum);
//Listeners
valueA.getDocument().addDocumentListener(new StealthListener(valueA , viewUpdateController) {
@Override
public void updateView() {
this.view.setText( Integer.toString( model.getA() ) );
}
@Override
public void updateModel() {
model.setA( Integer.parseInt( this.view.getText() ) );
}
});
valueB.getDocument().addDocumentListener(new StealthListener(valueB , viewUpdateController) {
@Override
public void updateView() {
this.view.setText( Integer.toString( model.getB() ) );
}
@Override
public void updateModel() {
model.setB( Integer.parseInt( this.view.getText() ) );
}
});
valueSum.getDocument().addDocumentListener(new StealthListener(valueSum , viewUpdateController) {
@Override
public void updateView() {
this.view.setText( Integer.toString( model.getSUM() ) );
}
@Override
public void updateModel() {
//Do nothing
}
});
//Initial Update
viewUpdateController.updateAllViews(null);
}
});
}
}
class DataModel {
private int a;
private int b;
private int sum;
public DataModel() {
a = 3;
b = 5;
Calculate();
}
public void Calculate() {
sum = a + b;
}
public int getA() { return a; }
public int getB() { return b; }
public int getSUM() { return sum; }
public void setA(int i) { a = i; Calculate(); }
public void setB(int i) { b = i; Calculate(); }
}
class StealthListener implements DocumentListener {
JTextField view;
GlobalUpdateController viewList;
private boolean silent;
public StealthListener(JTextField view, GlobalUpdateController viewList) {
this.view = view;
this.viewList = viewList;
this.silent = false;
this.viewList.add(this);
}
public void setSilent(boolean val) {
this.silent = val;
}
public void updateView() {
// Unique to each view, to be Overriden
}
public void updateModel() {
// Unique to each view, to be Overriden
}
public void update() {
//The silent flag is meant to avoid ListenerLoop when changing the document.
//When the silent is true is meant to listen to internal changes.
if(this.silent == false) {
updateModel();
this.viewList.updateAllViews(this);
}
}
@Override
public void insertUpdate(DocumentEvent e) {
update();
}
@Override
public void removeUpdate(DocumentEvent e) {
update();
}
@Override
public void changedUpdate(DocumentEvent e) {
update();
}
}
class GlobalUpdateController {
private ArrayList viewList;
public GlobalUpdateController() {
this.viewList = new ArrayList();
}
public void add(StealthListener control) {
this.viewList.add(control);
}
public void updateAllViews(StealthListener caller) {
for( StealthListener view : viewList) {
if( caller==null || view != caller ) {
view.setSilent(true);
view.updateView();
view.setSilent(false);
}
}
}
}
Мобильная версия