Студопедия  
Главная страница | Контакты | Случайная страница

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатика
ИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханика
ОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторика
СоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансы
ХимияЧерчениеЭкологияЭкономикаЭлектроника

Внутренние классы и структуры управления

Читайте также:
  1. IV. ОРГАНИЗАЦИОННАЯ СТРУКТУРА И ОРГАНЫ УПРАВЛЕНИЯ ОРГАНИЗАЦИИ.
  2. Static внутренние классы
  3. А) Внутренние болезни
  4. Абстрактные классы
  5. Абстрактные методы и классы
  6. Административные реформы. Изменения в системе государственного управления
  7. Аккумуляторная батарея служит источником напряжения 50 В для катушек аппаратов, осветительных и сигнальных ламп при неработающем генераторе управления.
  8. Алгоритм построения электронной структуры
  9. Алмазы, «международный терроризм» и новые способы управления глобальными рынками
  10. Анализ имущественного положения организации: цели, источники информации, методы и приемы, показатели оценки структуры баланса.

Более конкретный пример использования внутренних классов может быть получен в нечто называемом здесь как структуры управления.

Структура управления приложением это класс или набор классов, которые спроектированы для решения частных проблем. Для того, что бы применить структуру управления приложения, Вы должны наследовать от одного или нескольких этих классов и переопределить некоторые методы. Код, который Вы напишите в переопределенных методах, подстроит решения проблем под ваши конкретные задачи. Структура управления - частный тип структур управления приложениями с доминирующей функцией ответа на события; система, которая в основном занимается обработкой событий называется системой обработки событий. Одной из наиболее важных проблем в программировании приложений можно назвать графический пользовательский интерфейс (GUI), который почти полностью завязан на обработке событий. Как Вы сможете увидеть в главе 13, библиотека Java Swing - структура управления, которая элегантно решает проблемы GUI и очень много использует внутренние классы.

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

//: c08:controller:Event.java// Общие методы для любого контроля осбытий.package c08.controller; abstract public class Event { private long evtTime; public Event(long eventTime) { evtTime = eventTime; } public boolean ready() { return System.currentTimeMillis() >= evtTime; } abstract public void action(); abstract public String description();} ///:~

Конструктор просто запоминает время, когда Вы хотите запустить Event, затем ready() сообщает, когда приходит время для запуска. Естественно ready() должен быть переопределен в дочернем классе на нечто более другое, чем время.

Action() - метод, который вызывается, когда Event уже (готов) ready(), а description() дает текстовое сопровождение об этом Event.

Следующий файл содержит структуру управления, которая управляет и удаляет события. Первый класс - простой помощник, чья работа заключается только в содержании объектов Event. Вы можете заменить его любым подходящим контейнером, а в главе 9 Вы откроете для себя другие контейнеры, которые делают этот же трюк, но без какого либо дополнительного кода:

//: c08:controller:Controller.java// Вместе с Event, изначальная// система управления для всех систем управления:package c08.controller; // Это просто способ содержаня объектов Event.class EventSet { private Event[] events = new Event[100]; private int index = 0; private int next = 0; public void add(Event e) { if(index >= events.length) return; // (В настоящей жизни нужно обрабатывать исключение) events[index++] = e; } public Event getNext() { boolean looped = false; int start = next; do { next = (next + 1) % events.length; // Смотрим, не зациклился ли он: if(start == next) looped = true; // Если зациклился, то обнуляем список // if((next == (start + 1) % events.length) && looped) return null; } while(events[next] == null); return events[next]; } public void removeCurrent() { events[next] = null; }} public class Controller { private EventSet es = new EventSet(); public void addEvent(Event c) { es.add(c); } public void run() { Event e; while((e = es.getNext())!= null) { if(e.ready()) { e.action(); System.out.println(e.description()); es.removeCurrent(); } } }} ///:~

EventSet поддерживает Event -ов. (Если бы использовался настоящий контейнер из главы 9, то вам не нужно было бы беспокоиться о максимальном размере, поскольку он изменяет размер самостоятельно). Index используется для сохранения пути на следующее свободное место, а next используется, когда Вы ищите следующий Event в списке, что бы понять, не зациклились ли Вы. Информация о зацикливании особенна важна при вызове getNext(), поскольку объекты Event должны удаляться из списка (используется removeCurrent())как только они были выполнены, поэтому getNext() должен найти промежутки в списке и осуществлять выборку без них.

Заметьте, что removeCurrent() не просто устанавливает некоторый флаг, сигнализирующий, что объект уже не используется. Вместо этого он устанавливает ссылку в null. Это поведение достаточно важно, поскольку сборщик мусора не сможет очистить объект, на который все еще существует ссылка. Если Вы думаете, что ваши ссылки могут таким образом зависнуть, то тогда хорошей идеей устанавливать их в null, дабы дать возможность сборщику мусора удалить их.

Controller - то место, где на самом деле вся работа и происходит. Он использует EventSet для поддержки его объектов Event, а метод addEvent() позволяет вам добавлять новые события в список. Но все таки главным методом является run(). Этот метод циклически просматривает EventSet, выискивая объекты Event которые ready() (готовы) для обработки. Для всех объектов, которые готовы, он вызывает метод action(), печатает описание - description(), а затем удаляет событие - Event из списка.

Заметьте, что пока Вы ничего не знаете о том, что же на самом делают эти Event. И вот это и есть основная проблема проектировки; как отделить те вещи, которые должны изменяться от тех вещей, которые всегда постоянны? Или, если воспользоваться моими терминами - "вектор изменения", различные поведения различных типов объектов Event. Здесь Вы можете выражать различные действия созданием различных подклассов Event.

Как раз здесь в игру и вступают внутренние классы. Они предоставят в ваше распоряжение две необходимые возможности:

  1. Для создания полной реализации управляющей структуры приложения в едином классе, нужно инкапсулировать все уникальные части, которые реализуются. Внутренние классы используются для отображения видов action() необходимых для решения проблемы. В дополнение, следующий пример использует private внутренний класс, так что его реализация полностью скрыта и не может быть безнаказанно изменена.
  2. 2. Внутренний класс сохраняет эту реализацию от последующий трудностей, связанных с доступом к членам класса, поскольку из него есть полный доступ ко всем элементам внешнего класса. Без этой возможности код стал бы не очень приятным, и пришлось бы искать другое решение.

Рассмотрим частную реализацию структуры управления, спроектированную для управления функциями гринхауза (greenhouse functions)[43]. Каждое из действий полностью уникально и отличается от других: включение света и термостатов, выключение их, звон колокольчиков и рестартинг всей системы. Но структура управления просто изолирована в этом отличном (от других) коде. Внутренний класс позволяет вам получить множественно наследуемые классы от одного и того же базового класса (т.е. несколько наследников от одного в одном), Event, в одном единственном классе. Для каждого типа действия Вы наследуете новый внутренний класс от Event и пишите код контроля внутри action().

Как и для всех остальных структур управления, класс GreenhouseControls наследуется от Controller:

//: c08:GreenhouseControls.javaimport c08.controller.*; public class GreenhouseControls extends Controller { private boolean light = false; private boolean water = false; private String thermostat = "Day"; private class LightOn extends Event { public LightOn(long eventTime) { super(eventTime); } public void action() { // Сюда нужно поместить код управлением светом light = true; } public String description() { return "Light is on"; } } private class LightOff extends Event { public LightOff(long eventTime) { super(eventTime); } public void action() { // Сюда для выключения света light = false; } public String description() { return "Light is off"; } } private class WaterOn extends Event { public WaterOn(long eventTime) { super(eventTime); } public void action() { // сюда код управления water = true; } public String description() { return "Greenhouse water is on"; } } private class WaterOff extends Event { public WaterOff(long eventTime) { super(eventTime); } public void action() { // сюда код управления water = false; } public String description() { return "Greenhouse water is off"; } } private class ThermostatNight extends Event { public ThermostatNight(long eventTime) { super(eventTime); } public void action() { // Сюда код управления thermostat = "Night"; } public String description() { return "Thermostat on night setting"; } } private class ThermostatDay extends Event { public ThermostatDay(long eventTime) { super(eventTime); } public void action() { // сюда код управления thermostat = "Day"; } public String description() { return "Thermostat on day setting"; } } private int rings; private class Bell extends Event { public Bell(long eventTime) { super(eventTime); } public void action() { // Звонить каждые 2 секунды, 'rings' раз: System.out.println("Bing!"); if(--rings > 0) addEvent(new Bell(System.currentTimeMillis() + 2000)); } public String description() { return "Ring bell"; } } private class Restart extends Event { public Restart(long eventTime) { super(eventTime); } public void action() { long tm = System.currentTimeMillis(); // Конфигурация из текстового файла rings = 5; addEvent(new ThermostatNight(tm)); addEvent(new LightOn(tm + 1000)); addEvent(new LightOff(tm + 2000)); addEvent(new WaterOn(tm + 3000)); addEvent(new WaterOff(tm + 8000)); addEvent(new Bell(tm + 9000)); addEvent(new ThermostatDay(tm + 10000)); // Может быть добавлен объект рестарта addEvent(new Restart(tm + 20000)); } public String description() { return "Restarting system"; } } public static void main(String[] args) { GreenhouseControls gc = new GreenhouseControls(); long tm = System.currentTimeMillis(); gc.addEvent(gc.new Restart(tm)); gc.run(); } } ///:~

Заметьте, что light, water, thermostat и rings связаны с внешним классом GreenhouseControls и поэтому внутренние классы могут получать доступ к его полям без каких либо особенностей или специального доступа. Так же многие из методов action() осуществляют некоторый вид аппаратного контроля, который лучше всего перевести не в Java код.

Большинство из классов Event выглядят одинаково, но Bell и Restart особенны. Bell звонит и если он не звонит некоторое время, то добавляется новый объект Bell в список, так что он прозвонит позже. Заметьте, что внутренние классы почти что выглядят, как множественное наследование: Bell содержит все методы Event и он так же имеет доступ ко всем метода внешнего класса GreenhouseControls.

Restart ответственен за инициализацию системы, он добавляет все необходимые события. Естественно, лучше было бы отказаться от жестко зашитых событий в программе и читать их из файла. (Упражнение в главе 11 попросит вас сделать данное предположение.) Поскольку Restart() это просто другой объект Event, Вы так же можете добавить объект Restart внутрь Restart.action() так что система будет периодически рестартовать сама себя. И все, что нужно будет сделать это лишь в main() создать объект GreenhouseControls и добавить объект Restart который выполнял бы эту работу.

Резюме

Интерфейсы и внутренние классы более мудреная концепция, чем те, которые Вы можете найти в других ОО языках программирования. К примеру, ничего похожего в C++ просто нет. Сообща они решают некоторые проблемы, которые в C++ решаются путем множественного наследования. Но все равно в C++ использовать множественное наследование достаточно сложно, если сравнивать с Java, где интерфейсы и вложенные классы использовать гораздо проще.

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




Дата добавления: 2015-09-11; просмотров: 29 | Поможем написать вашу работу | Нарушение авторских прав

Множественное наследование в Java | Внутренние классы | Внутренний класс и приведение к базовому типу | Внутренние классы в методе и контексте | Анонимный внутренний класс | Связь с внешним классом | Static внутренние классы | Зачем внутренние классы? | Массивы | Массивы - первоклассные объекты |


lektsii.net - Лекции.Нет - 2014-2024 год. (0.009 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав