Это мой подход:
- Существует модель данных, которая обновляется из фоновых потоков.Вы не используете эту модель непосредственно из Swing.
- Модель данных уведомляет своих слушателей через события.Эти события содержат все, что нужно - ни один слушатель не должен вызывать модель данных для получения какого-либо значения.(примечание: для целей, не связанных с графическим интерфейсом, вы, возможно, в конечном итоге захотите сделать прямые вызовы для Модели данных, но для Swing вы определенно не хотите этого делать. В любом случае, это не обязательно - события содержат все)Один из этих слушателей, в свою очередь, обновит модель таблицы, но в потоке диспетчеризации событий, используя только информацию из события.
- Затем существует модель таблицы, которая предоставляет различные методы получения для JTable (getValueAt, getColumnCount)....).Инфо табличной модели содержит локально кэшированную копию модели данных, и эта копия обновляется только через входящие события, которые обрабатываются в EDT - потому что слушатель работает в EDT.Таким образом, очень важно не делать прямых вызовов к базовой модели данных, так как она обновляется из других потоков - к тому времени, когда JTable захочет значение для некоторой ячейки в строке X, эта строка может больше не существовать.Единственный способ получить реальные данные - опрос локального кэша.Таким образом, локальная копия модели данных также обрабатывается в EDT.Это важно, потому что после манипуляции с локальной копией вы обычно вызываете метод fireTableXxx (), чтобы позволить всем представлениям обновляться самостоятельно.Поскольку представления будут обновляться и в EDT, моделью таблицы нельзя управлять в этом временном окне: любой invokeLater (...) будет эффективно выполняться после завершения обновления таблицы.
- Представление,JTable, вызывает геттеры на TableModel на EDT.
- После регистрации прослушивателя он получит все необходимые события для синхронизации с моделью данных.
В целом, процесс обновления модели таблицы и обновления JTable (идругие виды, если таковые имеются), производится атомарная операция.Для этого у нас есть отдельная модель кэша, поддерживаемая нашей таблицей, которая обновляется только в EDT.Все, что связано с Swing, становится однопоточным, и, используя invokeLater (), мы уверены, что следующее событие будет обработано только после того, как текущее событие будет полностью обработано.
Как еще улучшить это:
- Отделите фактическую модель EDT от JTable и внедрите TableModel, делегировав вызовы модели EDT.Таким образом, вы можете использовать неограниченное количество слушателей Swing на одной модели EDT.Это замечательно, так как реализация моделей Swing (TableModel, ListModel, ComboBoxModel и т. Д.) - это очень маленькие простые и понятные реализации, и принцип DRY удовлетворяется - не повторяйте себя.Код модели EDT является централизованным и может использоваться повторно.Модели Swing становятся адаптерами и не сохраняют состояние.
- Каждая модель Swing так или иначе зарегистрирована в модели EDT.
- Модель EDT уведомляет каждую зарегистрированную модель Swing.Например, реализация AbstractTableModel будет реагировать на такое уведомление, уведомляя JTable, который прослушивает, вызывая
fireTableXxxChanged()
.
. В конце вы получите следующую цепочку:
- Вид сверху модели Swing (например, JTable)
- Модель Swing поверх модели EDT (например, AbstractTableModel)
- Слушатель, слушающий модель EDT,реагирование на события модели EDT путем отправки событий более высокого уровня (например,
tableModel.fireTableXxxChanged()
) - Модель EDT поверх параллельной модели.Эта модель фактически является вспомогательной моделью и не является ссылкой на «состояние бизнес-логики».Это фактический снимок действующей модели, предоставляющий согласованное неизменное состояние во время обновления компонентов Swing.Таким образом, модель EDT является вспомогательной моделью на уровне GUI.
- Слушатель, слушающий параллельную модель, обновляет модель EDT в потоке диспетчеризации событий. Этот слушатель может объединить несколько одновременно прибывающих событий в одно для повышения эффективности.
- Параллельная модель, которая просто не заботится ни о чем, связанном с Swing / EDT. Эта модель является чистой бизнес-логикой.
Этот подход позволяет полностью отделить графический интерфейс пользователя от бизнес-логики (здесь вы узнаете трехуровневую систему: GUI, бизнес-логика и постоянство), что является очень мощным. В хорошо спроектированной системе, где все команды выполняются на втором уровне, вы можете очень легко создать несколько контроллеров. Например, в графическом интерфейсе становится возможным управлять состоянием приложения, используя элементы управления Swing, а также создавать командную строку, в которой вы можете просто вводить команды. Это очень удобно для скриптового / автоматизированного тестирования бизнес-логики и того, как GUI реагирует на изменения в бизнес-логике.
В конце концов, это окупается, но определенно требует много дополнительной работы и усердного мышления, чтобы сделать это правильно.