Философия Java

         

Использование процессов для пользовательского интерфейса


Вот теперь появилась возможность разрешить проблему из примера Counter1.java с процессами. Решение заключается в правильном размещении подзадачи, т.е. цикла, расположенного внутри go(), который поместим внутрь метода run(). Когда пользователь нажимает кнопку start

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

//: c14:Counter2.java

// A responsive user interface with threads.

// <applet code=Counter2 width=300 height=100>

// </applet>

import javax.swing.*; import java.awt.*; import java.awt.event.*; import com.bruceeckel.swing.*;

public class Counter2 extends JApplet { private class SeparateSubTask extends Thread { private int count = 0; private boolean runFlag = true; SeparateSubTask() { start(); } void invertFlag() { runFlag = !runFlag; } public void run() { while (true) { try { sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if(runFlag) t.setText(Integer.toString(count++)); } } } private SeparateSubTask sp = null; private JTextField t = new JTextField(10); private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp == null) sp = new SeparateSubTask(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { if(sp != null) sp.invertFlag(); } } public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public static void main(String[] args) { Console.run(new Counter2 (), 300, 100); } } ///:~

Counter2 совершенно прямолинейная программа, основное предназначение которой в создании пользовательского интерфейса. Но теперь, когда пользователь нажал кнопку start, код обработки событий не вызовет метод, а будет создан процесс SeparateSubTask, после чего цикл обработки события Counter2 продолжиться.


Класс SeparateSubTask простое расширение от Thread с конструктором, который запускает процесс вызовом start(), а затем run(), который в сущности содержит код от go() из примера Counter1.java.

Из-за того, что SeparateSubTask внутренний класс, он может напрямую обращаться к JTextField t в Counter2; можно видеть как это происходит внутри run(). Поле t во внешнем классе определено как private, поскольку SeparateSubTask может получить к нему доступ без применения специальных разрешений, и всегда желательно делать поле настолько private, насколько это возможно, для того чтобы оно не могло быть случайно изменено извне вашего класса.

Когда нажимаем кнопку onOff она меняет runFlag внутри объекта SeparateSubTask. Данный процесс (когда он проверяет флаг) может самостоятельно остановиться или запуститься. Нажатие кнопки onOff вызывает тут же заметную реакцию. Конечно, в реальности реакция не мгновенная, счетчик остановится только тогда, когда процесс получит свой квант времени от CPU и проверит изменение флага.

Можно видеть, что внутренний  класс SeparateSubTask есть  private, а это значит, что к его полям и методам существует доступ по умолчанию (за исключением run(), который должен быть public поскольку он public в классе предка). Внутренний Private класс недоступен никому, за исключением Counter2 и эти два класса крепко связаны. Всегда, когда вы замечаете классы, которые оказываются крепко связанными друг с другом, рассмотрите возможность оптимизации своего кода и поддержки за счет использования внутренних классов.


Содержание раздела