Главная » C# » Создание ядра приложения управления освещением в Visual C# (Sharp)

0

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

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

Определение интерфейсов

Основной задачей контроллера освещения является управление освещением в лой комнате здания. Комнаты можно определить и организовать с помощью иерфейсов. Интерфейсов нам потребуется четыре. А именно:

• iRoom — интерфейс-заполнитель для концепции комнаты;

• iNoRemotecontroiRoom—  интерфейс  для  комнат,  которые  не  должны  упраяться контроллером;

• iRemotecontrolRoom—  интерфейс  для   комнат,   которые  должны   полностью управляться контроллером;

• isensorRoom—  интерфейс  для  комнат,  в  которых  освещение  управляется  стоянием (т. е. присутствием или отсутствием в них людей).

Интерфейсы   для   комнат   с   управляемым   освещением —   IRemotecontrolRoom и isensorRoom— будут зависеть от определенных логических механизмов. Эти интерфейсы должны предоставлять входные данные и принимать  выходные даые. Логический механизм также может получить другие данные, например время дня или уровень наружной освещенности. Все это сводится к определению некой логики, которая реализуется в ядре. Это является ключевым аспектом и подобно отношениям между родителями и детьми. В то время как родители воспринимают детей  как  разумных  существ,  способных  принимать  самостоятельные  решения, в конечном счете, окончательное решение принимается родителями. Таким же оазом, хотя ядро может принимать к рассмотрению ввод и предлагаемые решения, конечное решение оно принимает самостоятельно.

Интерфейс IRoom

Для целей разработки, самой простой и базовой концепцией является комната, корую можно определить следующим образом (в библиотеке контроллера LibLightingSystem):

public interface IRoom { }

Этот интерфейс не имеет никаких методов или  свойств,  и  называется  заполнитем. Заполняющий тип не имеет никакого другого назначения, кроме как для укания, что реализация относится к определенному типу. Применение интерфейсоаполнителей упрощает группирование объектов с конкретными возможностями.

Представьте определение объектов без помощи интерфейса-заполнителя.  Напрер, следующим образом:

class Typel { } class Туре2 { }

Из этих определений классов Typel и туре2 мы не видим никакой взаимосвязи мду ними. Не имеется ничего, что позволило бы нам заключить, что Typel и туре2 имеют  какие-либо  общие  атрибуты.  (Ладно,  технически  классы  взаимосвязаны в том, что оба являются производными класса object . Но такая взаимосвязь сродни

той, что каждый из людей является человеком.) Используя интерфейс-заполнитель, классы Typei и туре2 можно ассоциировать друг с другом таким образом:

class Typel : IRoom { } class Type2 : IRoom { }

IRoom[] rooms = new IRoom[10]; rooms[0] = new Typel(); rooms[1] = new Type2();

Реализуя интерфейс IRoom классами Typel и туре2 и при этом не делая ничего, кроме создания производных классов от IRoom, мы создаем взаимосвязь между классами Typel и туре2. Данная взаимосвязь заключается в том, что как класс Typei, так и класс туре2 являются комнатами. На данном этапе мы не знаем, какие комнаты они представляют, находятся ли эти комнаты в одном здании и т. п. Все, что мы знаем о них, — это лишь то, что они комнаты.

При разработке  ядра использование интерфейсов-заполнителей играет  очень ваую роль. Заполнитель указывает, что тип должен принадлежать группе. С помью группирования ядро может определить список сходных элементов. Это что-то сродни запрашиванию информации о возрасте у претендента на получение водельских  прав.  Информация о  возрасте не содержит никакой другой  информации о человеке, например пол, умственные способности или сведения о его способнтях к вождению машины.  Это всего лишь заполнитель, который указывает, явлтся ли данный человек членом группы, которая может получить водительское удостоверение.

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

Интерфейс  INoRemoteControlRoom

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

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

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

Определение интерфейса (в библиотеке контроллера LibLightingSystem), указающее, что освещение в данной комнате.не управляется контроллером, будет влядеть таким образом:

public interface INoRemoteControlRoom : IRoom { }

Так же как и интерфейс-заполнитель IRoom, интерфейс INoRemoteControlRoom не имеет ни методов, ни свойств. Но в данном случае методы и свойства отсутствт, т. к. для ядра они  не требуются.  Интерфейс  INoRemoteControlRoom служит для указания, что реализующий интерфейс тип является комнатой, но комнатой, освещение в которой не должно управляться контроллером. Используя спальню в качестве примера, реализация (определенная в проекте ноте) будет выглядеть таким образом:

class Bedroom : INoRemoteControlRoom {

}

Данное определение комнаты позволяет ядру использовать экземпляр комнаты слующим образом:

IRoom[] rooms = new IRoom[10]; rooms[0] = new Bedroom();

if (rooms[0] is INoRemoteControlRoom) {

// He делаем ничего и, возможно, изменяем направление исполнения кода.

}

Данный код создает массив комнат и присваивает элементу 0 массива экземпляр класса Bedroom. В операторе if выполняется проверка, не содержит ли элемент 0 массива экземпляр типа INoRemoteControlRoom.

ПРИМЕЧАНИЕ

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

Интерфейс  IRemoteControlRoom

Еще одним типом комнаты является комната, в которой освещение полностью управляется контроллером. Для таких комнат контроллер не рассчитывает на ввод с датчиков, а управляет освещением на основе логики, подходящий для определеой комнаты.

Например, для выставочного зала музея для определенных периодов дня освещие не требуется. А после окончания рабочего дня и уборки освещение может быть выключено вообще. В начале же рабочего дня освещение включается автоматички в определенное время. Такая простая логика может быть полностью реализова контроллером.

Интерфейс для комнат с освещением, полностью управляемым контроллером, оеделяется таким образом (в библиотеке LibLightController):

public interface IRemotecontrolRoom : IRoom { double LightLevel { get; }

void LightSwitch(bool lightState); void DimLight(double level);

}

Единственным вводом, предоставляемым интерфейсом IRemotecontrolRoom, явлтся информация о состоянии включенности освещения и его уровня.  Данная иормация предоставляется посредством свойства LightLevel. Это свойство доупно только для чтения (у него имеется лишь оператор get), т. к. в противном случае может произойти десинхронизация контроллера и уровня освещения.

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

ПРИМЕЧАНИЕ

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

Методы LightSwitch () и DimLight () класса IRemotecontrolRoom включают И влючают освещение и устанавливают его уровень, соответственно.  С  помощью этих методов осуществляется контроль состояния реализации.

Интерфейс ISensorRoom

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

быть, что контроллер должен выключить освещение опять. Но это будет неприльный  ответ.  Представьте  себе  ситуацию,  когда уборщик  включает освещение, а контроллер сразу же выключает его. Естественно, уборщик включает освещение опять, но и контроллер тоже знает свои обязанности и сразу же выключает его. Бучи мыслящим человеком, уборщик придумывает способ победить контроллер — закрепить выключатель во включенном положении, скажем, скотч-лентой. Это вывает непрерывное включение и выключение освещения, но поскольку интервал между противоположными состояниями измеряется в миллисекундах, то практички освещение остается включенным. Хоть такой подход и даст желаемый практеский результат, лучше было бы поставить на выключатель таймер, позволяющий освещению оставаться включенным на определенный период времени после  руого включения. Но и здесь есть своя проблема. Сколько времени должно освещие оставаться включенным?

Еще одним способом будет не использование таймера, а улучшение интерфейса, чтобы позволить  контроллеру узнать  состояние освещения.  Такой улучшенный  интерфейс, называющийся isensorRoom, определяется в библиотеке класса LibLightcontroller: public interface ISensorRoom : IRemoteControlRoom {

bool IsPersonlnRoom { get; }

}

Интерфейс isensorRoom имеет одно булево свойство IsPersonlnRoom. Значение true данного свойства означает, что в комнате  присутствуют люди;  в  протиом случае — значение false — комната свободна от людей. Каким образом реализация определяет наличие или отсутствие людей  в  комнате,  что  не  явлтся проблемой ядра, т. к. оно предполагает, что реализация знает, каким образом узнать это.

ПРИМЕЧАНИЕ

Как правило, ядро может взаимодействовать с реализацией  только  через  интерфейс. Ядро никогда не должно предполагать определенную реализацию интерфейса. Оно должно применять подход, при котором оно получает то,  что  видит.  Таким  образом, если ядру требуется дополнительная информация, необходимо спроектировать раирение интерфейса  или  реализовать  новый  интерфейс.  Конечно же,  это  не  означт, что необходимо расширять интерфейс для каждого возможного состояния. Иногда необходимо  определить  конкретный  интерфейс (iCanadianTaxEngine), как  в  приме с налоговым приложением в предыдущей главе.

Теперь, когда у нас имеются все готовые интерфейсы, можно приступить к реалации ядра.

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии