Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 79
Текст из файла (страница 79)
Кроме того, программа мелеет спросить Уоа! Ьох, менял ли пользователь значение с момента последнего запроса. Так как существует множество способов реализации этой базовой идеи, мы должны предположить, что может существовать много различных видов.Ьа! Ьох, таких как ползунки (з(Ыегз), простые диалоговые окна, в которые пользователь вводит числа, кнопки с чпсламн и ввод с голоса.
Общий полход состоит в построении «виртуальной системы пользовательского интерфейса» для использования в приложениях. Эта система предоставляет некоторые услуги, реализованные в существующих системах пользовательского интерфейса. Она должна быть реализована в широком наборе систем, поэтому следует учитывать переносимость кода. Естественно, существуют другие спосооы изоляции приложения от системы пользовательского интерфейса. Я выбрал этот подход, потому что он позволяет мне продемонстрировать множество технологий и методов проектирования. Эти методы использовались при построении «реальных» систем пользовательского интерфейса и, что наиболее важно, они применимы для решенги проблем.
далеко выходящих за рамки интерфейсных систем, 12.4.1. Традиционная иерархия классов Наше первое решение основывается на традиционной иерархии классов, существующей в э(п|п(а, эшаПга(к и ранних программах на С++, Класс Ува! Ьох определяет базовый интерфейс ко всем классам Иа! Ьох и вводит реализацию по умолчанию, которую более специальные вариантыУва! Ьох могут заместить. Кроме того, мы объявим данные, необходимые для реализации основного понятия: с!азв!иа! Ьох( рго1есгег( 1п! оа!, // значение !п! 1ош, 61аЬ; // нижняя и верхняя граница Ьоо1сдапяед; // и адика спор изменений риЬИа 1оа1 Ьох 1!пг П, 1пг ЬЬ1 ( сЬапяег( =Ха!ве, оа! - Рози = 11; 61яЬ = ЬЬ,) Глава 12. Производные классы 364 и!гтиа1!п1де1 иа!ие () (сбалдед=~а!яе; ге!игл иа1;) Ыг1иа! иоЫ яе1 иа!ие (т1 й ( сЬапдес! = 1гие; иа! = 1; ) и!г!иа! иоЫ геяе1 иа!ие (т1 !) ( сдапдес! =/а!яе; иа1 = Ь ) //для пользователя // для приложения итг(иа! иоп1 рготрг () ( ) и!г1иа! Ьоо1 яоая сдапуес! () солят ( ге!игл сЬапдес(; ) Реализация функций по умолчанию довольно небрежна и приведена здесь в первую очередь для иллюстрации их предполагаемого смысла.
В реалистичном классе, по крайней мере, производилась бы проверка диапазона значений. Программист мог бы использовать подобные «классы 1иа1> следующим образом: иоЫ тгегас1 (1иа! Ьох* рЬ) // побеспокоить пользователя рЬ вЂ” >рготр1 (), //... !п1! =рб->уе1 иа!ие (), Ц'(рЬ вЂ” >тая сьапдес1())( //новое значение; делаем что-нибудь е!яе ( /!' старое значение устраивало; делаем ччло-нибудь другое иоЫяоте 1с1() ( //!ьа! ярщегявляется производнимот !ио! Ьох !иа! Ьог.'р1 = песв1иа! я!Ыег(0, б); !п1егас1 (р1), 1иа1 Ьох'р2 = лет 1иа! ойа1 (1,!2); !л 1егас1 (р2); ) Большая часть кода приложения написана в терминах обычных 1иа1 Ьох (указателей на ннх), как например !л1егас1 (). При этом приложение не должно знать о потенциально большом количестве разнообразных 1иа! Ьох.
Знание о таких специализированных классах сосредоточено в относительно немногих функциях, создающих соответствующие объекты. Это изолирует пользователя от изменений в реализациях производных классов. Большая часть кода может не обращать внимания на факт наличия различных видов 1иа! Ьох. С целью упрощения обсуждения я не рассматриваю то, каким образом программа ожидает ввод. Возможно она действительно ждет ввода пользователя в де! иа1ие (), а может быть программа ассоциирует 1иа1 Ьох с событием и готовится отреагировать на обратный вызов (са))Ьас)с) или запускает фоновый поток для 1иа1 Ьох н затем опрашивает его состояние. Эти решения имеют первостепенное значение при проектировании систем пользовательского интерфейса. Однако хоть сколько-нибудь детальное их обсуждение отвлекло бы нас от демонстрации основных методов программирования и средств языка.
Описанные здесь методы проектировав!и, вместе с поддерживающими 365 12.4. Проектирование иерархий классов их средствами языка, не являются специфичными для пользовательских интерфейсов. Они применимы к гораздо более широкому диапазону задач. Различные виды 1оа! Ьох определены как классы, производные от Уоа! Ьох. Например: с!аяя!оа1 я!1«!ег риЫ!сйиа! Ьох( // графический внесший яид и т д. риЫ1с: 1иа! яИдег(га1, 1и1(; 1и1ае1 иа!ия ((; воЫ рготир1 (); )' Члены данных /оа! Ьох были объявлены защищенными для обеспечения доступа к ним из производных классов. Таким образом /оа! я11дегсде1 оа1ие ('1 может поместить значение в 1оа! Ьохсоа1, Защищенные члены доступны для членов самого класса и для членов производных классов, но не для других функций (см.
й !5.3). Кроме Еоа( я(и1ег мы можем определить другие варианты концепции Тиа( Ьох. Это могут быть )оа1 с(1а1, который позволяет выбирать значение нажатием кнопок, Р!аяЫлд !оа1 я!Ыег, заставляющий курсор ввода мигать при вызове рготр! () и Рорир гва! я1Мег, который в ответ на рготр1 () «всплывает» на экране где-нибудь в заметном месте, не позволяя пользователю его проигнорировать.
Откуда мы возьмем графику? Большинство систем пользовательского интерфейса имеют класс, определяющий базовые свойства сущности на экране. Поэтому если мы пользуемся системой от компании «Крутые Баксы» («В(В ВисЬ (пс.»), наши классы 1оа! я1(суег, /оа! гйа1 и т. д. должны стать разновидностью ВВихис1оти. Этого проще всего достичь, сделав наш 1оа! Ьох производным от ВВ!«г!ис!ою. Тогда все наши классы будут наследовать свойства ВВ»»г!ис(ото. Например, каждый /оа! Ьох можно отобразить на экране (в соответствии с принятым графическим стилем), изменить его размер, переместить и т. д. в соответствии со стандартами, принятыми в ВВоЫпс1ош.
Наша иерархия классов будет выглядеть следующим образом: с1аяя1иа! Ьох риЫ!сВВиииГоси(/* ... */), //иереиисая для исиол»зевания // с В8% идош с!аяя !оа! я!Иег: риЬВс ряа! Ьох ( /* ... '/ ), с1аяя1оа1 с!!а1; риЫ!с!оа! Ьох(/'... '/); с!аяяг!аспид 1оа! я!1дег риЫ1с!оа! я!1с(ег(/'., "/); с1аяя Рорир (иа! я!1сГег.риЫ1с!оа! я!1с!ег(/* ... '/); нли в графическом виде; ВВто~~ Иота Уоа1 Ьох Рорир !оа1 яВг1ег Наяй(аа гоа1 я1Ь(ег Збб Глава 12.
Производные классы 12.4.1.1. Критика Изложенный подход к проектированию хорошо работает во многих случаях и соответствующая иерархия является хорошим решением для многих проблем. Однако имеются и некоторые неудобства, которые заставляют нас искать альтернативы. Мы сделали ВВш!ас(ош базовым классом 1оа! Ьох. Это не совсем правильно. Использование ВВш!ас(ош не является частью нашего фундаментального представления о !иа1 Ьох — это деталь реализации. Объявление Ура! Ьох производным от ВВш1ис!ош привело к выходу деталей реализации на первый план в процессе проектирования. Бывает, что такой подход приемлем.
Например, использование среды от «Крутых Баксов» может быть неотъемлемой частью бизнес-процесса нашей организации. Но что если мы захотим реализовать(эа! Ьохдля систем от «Имперских Бананов» («(шрепа! Вапапаз»), «Освобожденного программного обеспечения» («! 2(зета(ед Бо(бмаге») или «Шустрого Компилятора» («Сошр(!ег Ъ'(з(ххез»)? Нам бы пршплось поддерживать четыре разные версии нашей программы; с!аееуиа! Ьох:риЫтсВВигиг(ого(/'...'/); с!оее !иа! Ьох; риЫ!с С!»го!иИош ( /' ...
'/ ); с!аее !оа! Ьох !«иЫ!с1Вихш!ош ( /' ... '/); с!аее!оа! Ьох, риЫ!сйБ«о!иг(ого(/" ... '/); Наличие нескольких версий может превратиться в настоящий копыар. Другая проблема состоит в том, что все производные классы совместно используют базовые данные, объявленные в Уиа! Ьох, Эти данные тоже, конечно же, являются деталью реализации, вкравшейся в интерфейс 1эа! Ьох. С практической точки зрения этп данные во мнопзх случаях вредны.
НапримерДва1 з1!с(ег не нуждается в отдельно хранимом значении. Его легко можно вычислить по положению ползунка при выполнении пе! иа1ие (!. Как правило, хранение двух взаимосвязанных, но различных наборов данных, провоцирует ошибки, Рано или поздно будет нарушена синхронизация. Кроме того, как показывает практика, новички склонны к объявлению в качестве защищенных избыточного количества данных, что приводит к проблемам при сопровохщении, Данные лучше иметь закрытыми, чтобы при написании производных классов не возникала путаница.
А еще лучше, когда данные находятся в производных классах, где онн могут быть определены наилучшим образом в соответствии с требованиями и не могут усложнить жизнь другим производным классам, не связанным с ними. Почти во всех случаях защищенный интерфейс должен содержать только функции, типы и константы. Выигрыш от использования ВВш!и!1ош в качестве базового класса состоит в том, что средства, реализованные в нем, становятся доступными пользователям /эа! Ьох. К сожалению, это также означает, что изменения, сделанные в классе ВВш!ас!ош, могут вынудить пользователей перекомпилировать или даже переписать код для восстановления работоспособности после этих изменений.