Главная » C++, C++ Builder » Процесс разработки компонентов в CBuilder

0

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

·                       Определите проблему, которую пытаетесь разрешить.

·                       Найдите частное решение этой проблемы.

·                       Сделайте решение более глобальным.

·                       Спроектируйте компонент для осуществления глобального решения.

·                       Сделайте компонент как можно более гибким.

·                       Воплотите идею компонента в жизнь.

·                       Отладьте компонент.

·                       Протестируйте компонент.

·                       Создайте документацию по интерфейсу компонента для конечного пользователя (то есть программиста, который будет использовать этот компонент).

·                       Проделайте работу по представлению компонента (файлы помощи, иконки, определение

палитр и т. п.).

·                       Проинсталлируйте  компонент.

·                       Используя накопленные знания, вернитесь к первому шагу.

Как вы видите, разработка компонентов это непрекращающиеся поиски решения и формулировки проблемы. На самом деле шансы за то, что в день окончания работы над компонентом вы возьметесь переписать все заново, достаточно велики, так что в последнем пункте плана гораздо меньше юмора, чем кажется на первый взгляд. В своей исторической книге по разработке программных продуктов «The Mythical Man-Month» Фрэд Брукс призывает писать первую версию программы изначально как черновик, поскольку все равно придется переписывать. Подобный подход вполне подходит для разработки компонентов. Простые на первый взгляд шаги проектирования, разработки и отладки, а затем и использования компонента могут завести вас очень далеко в сторону по сравнению с вашей изначальной идеей. Если вы решили, едва закончив, переписать компонент заново (используя, естественно, многое из первой попытки, что существенно облегчит задачу), то, скорее всего, во второй раз  результат понравится вам куда больше. Компоненты и приложения, не переписанные после самой первой версии заново, обычно в результате доделываются так ужасно, что даже сам их разработчик не может опознать свое детище. Уж поверьте мне. Я сам пробовал столько раз, что теперь даже не думаю, что у меня что- нибудь получится с первого раза.

Процесс разработки компонентов в деталях

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

Определение проблемы, которую вы хотите разрешить

Трудно сосчитать, сколько раз я сталкивался с кодом, содержащим ошибки. Ошибки варьируются от абсолютно идиотских (типа деления на 0) до прямо-таки грандиозных, связанных с тонкостями внутреннего устройства  операционной системы Windows (а иногда даже и MS-DOS).  И все же большинство ошибок возникает из-за того, что программист не до конца понимает, чего же он хочет добиться.

Написание компонентов не только сродни написанию любого другого кода, но и имеет дополнительные особенности. Если вы сделаете ошибку в описании метода одного из созданных вами классов C++ в своем приложении, возникнут серьезные проблемы, но они будут ограничены приложением, в котором появились. Если вы в какой-то момент обнаружили, что вам требуется дополнительные, или просто другие, параметры для метода, вопрос только в изменении описания метода и последующем изменении приложения. Однако если ваш класс  является  частью библиотеки, то выяснится, что изменение отразится на нескольких приложениях, использующихся в вашей компании. Теперь, если обратиться компоненту, то станет ясно, что одно мельчайшее изменение может затронуть сотни приложений, разработанных десятками компаний (если предположить, что вы кому-то продали свой компонент). Из-за таких вот маленьких исправлений мы и сходим с ума.

Садясь за написание компонента, первым делом напишите формулировку проблемы, которую хотите разрешить. Формулировка может быть очень простой, типа «Служит для вывода на экран изображений в формате JPEG», или очень сложной, типа «Служит для осуществления взаимодействия между многими компонентами на форме, для оповещения каждого об изменении

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

«Создание компонента, позволяющего  перемещаться  от одной части текста подсказок к  другой

посредством щелчка мыши».

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

Определите частное решение проблемы

После того, как у вас появилась формулировка проблемы, вы можете определить путь решения этой проблемы. На данной стадии разработки вам не надо думать обо всех проблемах, с которыми компонент поможет справиться, главное, чтобы он решал поставленную задачу. Если, например, формулировка проблемы заключалась в перемещении между кусками файлов помощи, решение может быть определено, как позволяющее пользователю переместиться из точки A в точку B при нажатии на точку С.

Зачастую воплощение в жизнь частного решения проблемы — это все, что вам понадобится. Вы можете никогда не перейти к следующему шагу (обобщению), но вы, по крайней мере, рассмотрите проблему и представите письменное заключение по ней. После того, как вы нашли частное решение проблемы, можете вынести его на обсуждение коллег. Может статься, вы разрешили проблему, а может быть, ваше решение не работает, принимая во внимания все обстоятельства. Например, вашим решением могла стать  проверка слова под курсором и перескакивание в другой файл справки, содержащий это слово. Ваши коллеги могли бы указать вам на то, что перескакивание со слова «тег» из документа по программированию страниц HTML в документ об основах программирования — это вряд ли то, чего хотел бы пользователь. Разработка частного конкретного решения может так же вынудить вас вернуться на шаг назад и пересмотреть формулировку проблемы.

Выработка обобщенного решения

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

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

Фрэд  Джонс  столкнулся  с  проблемой  при  написании  приложения  на  CBuilder.  Ему  требуется

ограничить вводимые в некоторые поля редактирования значения только числовыми.  Значения могут быть любой длины, но они должны быть положительными и могут содержать только цифры и десятичную точку. Никаких шестнадцатиричных представлений, никакого экспоненциального вида и т. д. и т. п. Пользователю позволяется вводить лишь десятичные цифры от 0 до 9 и десятичную точку. Фрэд сразу же принимается за работу, наспех увязывая между собой строки кода из некой гениальной книги, которую он по счастью купил только накануне (и которую вы сейчас читаете), и в результате получает -таки функцию в форме, которая как раз и делает то, что надо. Фрэд доволен, пользователи довольны, программа работает отлично, в общем, мир стал немного лучше.

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

Решение создать новый компонент принято, и Фрэд прилежно работает над этим. Очень скоро готов компонент, который проверяет, число ли введено в поле редактирования, и отвергает все, кроме чисел. Примерно две недели Фрэд ходит счастливый. После чего  выясняется,  что  поля номера социального страхования должны позволять использование цифр и тире, поля дат — цифр и косых черточек, а поля специального регистрационного номера — и цифр, и букв. Фрэд подавлен.

Что же не правильно? Фрэд решал частную задачу по написанию компонента для фильтрования данных для конкретного поля. Но, к сожалению, Фрэд не осуществил следующего шага — обобщения решения. Если бы он рассмотрел проблему чуть более широко, он бы  понял,  что гораздо проще было бы разрешить пользователю самому задавать фильтр для поля — множество допустимых символов. Вы можете удивиться,  почему Фрэд  не  использовал  компонент  Masked Edit, поставляющийся с CBuilder. Дело в том, что этот компонент не предоставляет той гибкости, которая нужна Фрэду. Маска задается жестко один раз, так что если бы я захотел ввести 1.234, или 123.4, или 12.45, я бы не смог написать маску, которая позволила бы это. Компонент — поле редактирования с фильтром, воспринимающий значения 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, сможет сделать все, что надо. Как только будут появляться новые требования, Фрэду надо будет изменить значение фильтра, чтобы добавить новый компонент на новую форму.

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

Проектирование компонента для воплощения обобщенного решения

Теперь, когда вы знаете, что именно вам надо сделать, наступает самый критический момент во всем процессе создания компонента — проектирование компонента для разрешения поставленной проблемы.  Если  вы  правильно  спроектировали  компонент и  правильно  наметили  шаги  для  его

воплощения,  собственно написание компонента станет достаточно простой вещью.

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

—  там обычно  нанимают  профессионального проектировщика,  который разрабатывает  внешние

спецификации программы. Проектирование это одна из тех вещей, которым приходится учится методом проб и ошибок.

Когда вы проектируете компонент, перед тем, как делать что-либо другое, вам надо определить три вещи — свойства, методы и события компонента. К сожалению, то, насколько хорошо вы это сделаете, зависит, в основном, от количества спроектированных вами ранее компонентов и того, насколько хорошо вы представляете себе компонент, над которым собираетесь работать. Это дело практики и только практики. Методы компонента, так же как и свойства, должны быть хорошо продуманы. Методы должны быть в состоянии делать то, чего потребует от них пользователи (а может, свойства должны быть в состоянии — в CBuilder границы между ними часто размыты), иначе интерес к компоненту сразу же иссякнет. Кроме того, вам надо определить все события, которые должны обрабатываться компонентом. Проектирование событий —  наиболее  трудная часть всего процесса, и к ним часто приходится возвращаться. Очень часто многие события добавляются в компонент в его новых версиях; свойств и методов добавляется,  как  правило, гораздо меньше. Это зависит от того, как именно компонент используется программистом.

Сделайте проект компонента наиболее гибким

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

Например, если вы создаете компонент для работы с датами, у вас, естественно, будут присутствовать свойства для числа, месяца и года. Это будет частным решением проблемы. Так же у вас, скорее всего, будет существовать метод для представления даты в виде строки, чтобы пользователь мог ее видеть. Этот метод отображения имеет зависимость, которая не очевидна с первого взгляда. Для отображения строки вам нужна дата и формат вывода. Формат не задан в частном решении, так что мы добавим его в обобщенное решение. Формат будет состоять из типа отображения даты (ДДММГГ, ММДДГГ, ДДММГГГГ, или какого-либо еще) и разделителя для составляющих даты (обычно это / или -).

Воплощение проекта компонента

Теперь, когда у вас в голове (или, что лучше, на бумаге) есть проект компонента, наступило время для самого занятного — собственно воплощения компонента в виде кода. CBuilder предоставляет некоторую помощь для этого, но большую часть работы придется сделать все-таки вам. Сначала вам надо сделать скелет компонента, содержащий все свойства, методы и события, необходимые в соответствии с проектом.

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

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

Мы не будем много говорить о процессе разработки компонентов, а вместо этого сфокусируем основное внимание на тех его частях, которые в противном случае вам пришлось бы пересматривать во время разработки. Большая часть главы посвящена разработке кода, а не разговорам.

Отладка компонента

После того, как разработка компонента закончена, начинается его отладка. В этой части мы посмотрим, как изначально отладить компонент, и как работать с ним после инсталляции.

Отладка относится к области магии, причем, скорее всего, к черной. Если вы научитесь некоторым приемам отладки, вы можете далеко пойти в мире создания программных продуктов. Сделайте отладчик CBuilder своим другом, а не врагом, и вы обнаружите в нем несколько очень мощных инструментов.

И последнее замечание перед тем, как двигаться дальше. Отладка — это не тестирование. Отладить компонент — это значит сделать так, чтобы он делал то, что он должен  делать  по замыслу. Если я заявляю, что мой компонент рисует линию от одного своего угла до другого, он именно это и должен делать. Отладка — это проверка, что он действительно это делает. Тестирование же это совсем другое.

Тестирование  компонента

Как я только что сказал, отладка и тестирование это разные вещи. Так что же такое тестирование? Это проверка не только того, что компонент делает вмененные ему в обязанность вещи, но и того, что он не делаем ничего такого, что делать в принципе не должен. Отладка — это проверка того, что правильно введенные значения повлекут за собой правильный результат, а тестирование — это зачастую проверка того, что неправильно введенные значения так же приведут к правильному результату (или правильной работе).

Когда вы создаете компонент, вы должны очень широко его протестировать. Это означает гораздо большее, чем проверка  того, что введенные значения  вызывают  правильную реакцию.  То  есть если я постулирую, что некоторое поле должно лежать в пределах от 1 до 10, то ввод 11, 0 или – 32768 не должен вызывать проблем. И если одно свойство зависит от другого, то компонент не должен обрушиться, если второе поле не задано. Это означает проверку граничных условий для свойств и методов.

Граничные условия — это экстремальные ограничения на вводимые значения. Если число должно быть целым и положительным, то для него граничными значениями будут 0, -1, 32767 и -32768. Вам также надо выбрать одно из лежащих между ними допустимых значений, например 1234, и проверить, все ли работает как должно. Если у вас используется какой-нибудь сложный алгоритм, в котором возникают проблемы со значениями внутри допустимого диапазона, то тестирование приведенных выше пяти значений будет более полезным, чем просиживание по 12 часов за клавиатурой и тестирование все подряд значений, какие только придут в голову.

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

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

Честное предупреждение: большинство программисты, как правило, тестируют весьма плачевно. Если вы действительно хотите, чтобы ваш компонент был хорошо протестирован, пусть вам помогут другие люди (это называется бета-тестирование).

Описание интерфейса компонента

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

Имейте в виду, что если вы не опишите свой компонент, то им никто не воспользуется. У людей просто не будет времени на изучение исходного кода в поисках подсказки, как что-либо сделать. И на то, чтобы разгадать допустимые значения для свойств времени тоже не будет. Фирма Borland сделала использование компонентов в CBuilder настолько простым, насколько это только было возможно без принесения в жертву гибкости и мощи самих компонентов. Так что только от вас зависит создание максимально дружелюбных к пользователю компонентов.

Не перегружайте пользователя документацией. Делайте вашу документацию простой и сжатой, но в то же время понятной. Например, если вы разрабатываете компонент для работы с датами, то можете запостулировать, что все поймут, что представляет свойство Month (месяц), не так ли? А вот и нет. В том, лежат ли значения, обозначающие месяцы, в пределах от 1 до 12 или же от 0 до 11, есть огромная разница. Ваша документация обязана донести эту информацию до пользователей, тем более, что это может выглядеть очень просто, например:

Month: Номер месяца в дате; значения целые, от 0 до 11.

Эта строка сообщит мне все, что я хочу знать. Составляющее Day (день) вообще не является таким уж очевидным. Не понятно — это день недели или день месяца? Присваивание свойствам более определенных имен существенно облегчит жизнь. Назовите свойство DayOfMonth (день месяца), и никто уже не подумает, что это день недели. Правда, вопрос о том, отсчитываются ли значения этого свойства от 0 или от 1, все еще останется открытым. Будьте последовательны, и люди вас поймут. Лучшим  комплиментом  для  создателя компонентов может служить тот факт,  что программист не смотрит в документацию потому, что все компоненты работают по одному принципу.

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

Разнообразная мелкая работа

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

Инсталляция  компонента

Хотя инсталляцией компонента занимается среда разработки (IDE), для вас тоже есть чем заняться. Как мы увидим в этой главе, в CBuilder есть два варианта тестирования компонента. В основном вы будете тестировать компонент в его изначальном виде — до инсталляции (это займет гораздо меньше времени). В этой главе я покажу, как тестировать компонент до инсталляции. Однако после завершения инсталляции вам придется протестировать компонент еще раз (регрессивное тестирование, помните?). Компоненты вовсе не всегда ведут себя после инсталляции так же, как во время изначального тестирования. Вам надо обязательно удостовериться, что в обоих случаях компонент работает корректно.

Совет

Может показаться, что нет способов сынсталлировать компонент, не имея исходного кода или объектного файла для него. Это не совсем так. Неправда и то, что можно сынсталлировать в VCL CBuilder только один компонент за раз. Если вы выйдете из IDE и создадите библиотечный файл (library file) для своих компонентов, то сможете добавить сразу несколько компонентов, либо компонент, состоящий из нескольких файлов. Правда, этот метод потребует дополнительных усилий. Вам придется добавить в свой компонент новый модуль с именем, совпадающим с именем библиотечного файла, содержащий функции Register для всех компонентов библиотеки. Для того, чтобы понять, как это должно выглядеть, обратите внимание на библиотеку comps.lib, находящуюся в директории прилагаемого компакт-диска, относящейся к данной главе. Вы найдете там модуль comps.cpp, содержащий функцию Register для этой библиотеки, заключенную в отдельную именованную область (namespace).

С новыми силами — все с начала!

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

Итак, теперь вы знаете все, что надо, о разработке компонентов, не правда  ли?  Может  быть. Давайте попробуем свои силы и прямо сейчас создадим несколько настоящих компонентов. Мы начнем с простого и будем постепенно усложнять задачи, как обычно.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

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