Дж. Хэнк Рейнуотер
    Январь 2002

Приложение А
Как ухаживать за живностью – электронный администратор

   У программ с открытым исходным кодом есть много достоинств и совсем немного недостатков. Как известно, главное для нас – стандарты. В то же время при наличии исходного кода у нас появляется возможность вводить собственные стандарты. Я решил предоставить в ваше распоряжение исходный текст электронного администратора – быть может, с ним вы сможете добиться успеха чуть быстрее, чем без него. Должен предупредить, что эта программа предназначена исключительно для личного пользования – соответственно ее нельзя перепродавать, а также сначала переделывать, а потом перепродавать. Для ее загрузки зайдите на сайт http://www.piter.com.
   Электронный администратор (подробнее см. главу 4) написан в среде Visual Basic 6.0 с установленным служебным пакетом 5. В качестве базы данных применяется Access 2000. Помимо стандартных элементов VB, я обращался к следующим средствам:
   • интерфейсы источников данных от Microsoft;
   • библиотека Microsoft ActiveX Data Objects 2.6;
   • Crystal Reports версии 8.5 (отдельные компоненты) [139];
   • Microsoft Direct Speech Synthesis;
   • Microsoft Agent Control 2.0;
   • Microsoft Scripting Runtime;
   • Microsoft Direct Speech Recognition.
   Помимо стандартных компонентов, в этом проекте я обратился к довольно древнему элементу управления календарем, созданным MSCAL.OCX [140]из поставки Access 97, и специальным элементом управления датами под названием MyData.OCX, который вы можете скопировать с сайта вместе с исходным кодом.
   Можете заменять ссылки и компоненты как вам заблагорассудится. Только не забудьте соответствующим образом скорректировать код.
   INI-файл регулирует конфигурацию программы и жестко задает структуру каталогов. Каждому «ресурсу», определенному в таблице базы данных с аналогичным именем, должен соответствовать каталог для экспорта отчетов. В каталоге с шаблонами содержатся задействованные в программе файлы Crystal Reports. Стоит только открыть самораспаковывающийся файл с кодом, и он автоматически создаст нужную структуру каталогов. В тот же момент база данных наполнится шаблонными данными.
   В базе данных есть ряд таблиц соответствия, которые вы можете заполнить так, как сочтете нужным. На то, чтобы сделать еще несколько таблиц, мне элементарно не хватило времени – в частности, я не добрался до таблицы проектов, которую можно было бы соединить с таблицей заданий. Это решение, надо сказать, упростило процесс генерации отчетов средствами программы Crystal Reports, а потому имейте в виду: любое изменение скажется на шаблоне отчетов. Если хотите, можете изменить мой код – мне, конечно, будет интересно увидеть результат. Возникнут вопросы – пишите на адрес herdingcats@mindspring.com. Постараюсь на все отвечать.
   А теперь – несколько слов о дополнительных экранах программы, которые способны оказать существенную помощь в деле организации личных информационных потоков. На рис. А.1 изображено родительское окно.
   Экран Today, показанный на рис. 4.3 в главе 4, несомненно, важен для систематизации административных функций, однако им одним программа не исчерпывается. Согласно моей теории планирования руководящей деятельности, любое задание нужно рассматривать в трех основных измерениях: Project (Проект), Source (Источник) и Assigned (Назначенные задания). Представление Project (рис. А.2) помогает отслеживать ход выполнения всех заданий в рамках проекта. Представление Assigned (рис. А.З) демонстрирует распределение заданий между сотрудниками. Наконец, представление Source (рис. А.4) напоминает о том, кто (например, какой процесс или комитет) требует результата от вас. Все это дочерние окна интерфейса MDI с изменяемым размером, поэтому открывайте их столько, сколько хотите.
   По информации с каждого из этих экранов генерируются отчеты, которые можно отправить по почте сотрудникам, принести на собрание, предоставить начальнику, просмотреть на предмет согласования ваших планов с деятельностью других подразделений компании. До тех пор пока приложения для коллективной работы не достигнут определенного уровня зрелости, несогласованность в действиях между отделами при отслеживании разных типов заданий будет оставаться обычной практикой – при этом каждый из отделов будет навязывать собственный стиль руководства. Поэтому для управления списками заданий часто привлекается метод «вырезания и вставки», а управленческие реалии, которые больше напоминают страшный сон, некоторые именуют не иначе как «казнь через разрезание на тысячу кусочков».
Рис. А.1. Родительское окно
 
Рис. А.2. Представление Project
Рис. А.З. Представление Assigned
Рис. A.4. Представление Source
   Три окна со списками, расположенные по периметру родительского окна (см. рис. А.1), предназначены для быстрого доступа к трем основным представлениям. В этих окнах содержатся списки имен проектов, ресурсов, которыми можно назначить новые задания (в основном это имена людей), и источников заданий, над которыми вы работаете или которые отслеживаете. По двойному щелчку на любом пункте списка на экране появляется новое представление, в котором задания рассортированы в соответствии с назначением представления.
   Я предпочитаю выделять отдельный элемент Source для отслеживания тех заданий, которые передо мной ставит моя многоуважаемая руководительница. Забыть предписание начальницы – это же так глупо (не говоря уже о том, что этот опрометчивый поступок ставит под угрозу карьерные перспективы)!
   Выполнение функций, представленных в открываемых щелчком правой кнопкой мыши контекстных меню, обеспечивается несколькими объектами-контейнерами. Испробуйте их или хотя бы взгляните на соответствующий код обработки событий.
   Ну и все на этом. Хотите узнать больше – поройтесь в исходном тексте. Забавляйтесь с живностью, но не обижайте ее!

Приложение Б
Как дать скотине в глаз – критический обзор кода электронного администратора

   В главе 6, в разделе «Кодовая полиция», я объяснял, как стать кодовым полицейским. Сохранять объективность при чистке собственного кода очень сложно, но я постараюсь. Связав в названии этого приложения свой код со скотным двором, я надеялся подсказать вам, что я собираюсь делать. Все, что я хочу, – это чтобы вы разобрались в процессе критического обзора кода и обратили внимание на некоторые моменты, которые играют существенную роль в процессе проверки кода, написанного подотчетной вам группой.
   Имея перед глазами исходный код, вам будет легче читать это приложение. Как я уже говорил в приложении А, скачать код можно с сайта http://www.piter.com.

Контекст и происхождение программного продукта

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

Правила игры

   Анализировать код я намерен по методике, изложенной в главе 6. Итак, как я уже говорил, хороший код отличается следующими особенностями.
    • Он пишется в соответствии со стандартами программирования, принятыми для конкретного языка.В таком случае применяемые разными программистами методики конструирования объектов, обусловленные архитектурой, не будут слишком разниться.
    • Внутри объектов соблюдается строгая связность.Объект – это несколько больше, чем просто группа процедур; он должен выполнять конкретную функцию. Как известно, сердце не дышит, а легкие не качают кровь.
    • Взаимозависимость между объектами по возможности минимизируется.В большинстве случаев (в отсутствие существенных доводов в его пользу) взаимозависимость не приводит ни к чему хорошему – она лишь усложняет сопровождение. На последующую изоляцию взаимозависимых объектов затрачиваются серьезные финансовые и временные ресурсы.
   Я обращу ваше внимание на ряд слабых сторон кода. По большей части они обусловливаются сжатыми временными рамками и тем обстоятельством, что инструмент, к которому этот код относится, я проектировал исключительно под себя. Пока что я ни разу не утверждал, что безгрешен, поэтому мои самоистязания вряд ли кого-нибудь удивят.

Следовал ли я стандартам?

   В основном я следовал стандартам – по крайней мере, мне так кажется. VB я пользуюсь, начиная с версии 1.0, и с годами отношения с кодом складывались у меня (наверное, так же как и у вас) по-разному – был и удачный, и провальный опыт. С моей точки зрения, следование стандартам VB выражается в попытках привести объектно-ориентированные понятия в соответствие с этим языком, который, по правде говоря, отнюдь не полностью поддерживает объектно-ориентированную парадигму.
   В главе 4 я изложил понятие задачи, или задания, – основного организующего принципа программного обеспечения. Просмотрев мой код, вы найдете в нем объект под именем clsTasks и вспомогательный объект clsTask. В этих двух модулях классов инкапсулированы пользовательское взаимодействие и все данные, обрабатываемые программой в связи с заданиями. Формы frmTask и frmTasks, ответственные за обработку заданий на стороне графического пользовательского интерфейса, являются дочерними объектами объекта clsTasks. Все прочие объекты, например clsToday, при обработке заданий обращаются к локальным экземплярам clsTasks. Эта схема довольно удачно, как мне кажется, иллюстрирует методику многократного использования объектов.
   Модульные объявления объекта clsTasks выглядят так:
   '-Закрытые объекты и события
   Private mo_DataService As clsDataService '–данные объекта
   Private mo_PickList As clsPickList '–список отбора для форм
   Private WithEvents mfjasks As frmTasks '–все задания, связанные с frmTasks
   Private WithEvents mfjask As frmTask '–отдельное задание, связанное с frmTask
   Private mo_DataGrid As DataGrid
   Private WithEvents mo_DataProvider As clsDataProvider '–основные данные
   Private ml_CurTaskID As Long '–выбранный идентификатор задания
   Private ms_Project As String '–применяется с frmProject
   Private mo_ProgConfig As clsProgConfig
   Private ms_TaskFilter As String
   Private mo_Task As clsTask
   Private mb_NeedRefresh As Boolean
   Private ms_Resource As String
   Private ms_Source As String '–открытые объекты и события
   Public Event TaskUpdatedO
   И что мы имеем? Много комментариев, среди которых нет ни одного, который описывал бы назначение центрального объекта. Плохой код и бездарный кот! И каким же образом человек со стороны сможет понять, зачем этот объект нужен, если нет никаких описаний?! Для этого придется основательно изучать код. Я-то его знаю вдоль и поперек, а вот свежий взгляд наткнется на труднопреодолимое препятствие.
   Будь вы менеджером, вы бы, конечно, настояли на введении заголовка модуля с указанием его автора и даты создания. Кроме того, вы, вероятно, потребовали бы от программиста составить обзор модуля, обозначив в нем имена открытых процедур и механизм «жизнеобеспечения» ими объекта.
   Нельзя, однако же, не признать некоторые достоинства вышеприведенного фрагмента кода. Обратите внимание на имена переменных – m в них идентифицирует область действия (модуль), а следующий символ обозначает тип переменной (Соответствует длинной переменной, s – строковой, b – логической, и т. д.).
   Теперь рассмотрим конкретную процедуру, инициирующую процесс отображения формы задания:
   Public Sub Show(Optional sResource As String ="")
   If (mf_Tasks Is Nothing) Then
   SetHourglass
   Set mf_Tasks = New frmTasks Load mfjasks
   Set mo_DataGrid = mf_Tasks .grdTasks
   '-load tasks
   LoadTaskGrid
   '-Load resource combo
   mo_PickList.LoadPickList mf_Tasks.cboResource. PIC_RESOURCE
   ' -configure task list
   ms_Resource = sResource
   mf_Tasks.Configure ms_Resource
   If sResource «» "" Then   ' -установка источника данных для отображения отдельного задания
   ms_TaskFilter = "Assigned =" & Chr$(39) & sResource & Chr$(39)
   mo_DataProvider.Filter ms_TaskFi1ter
   mo_DataProvider.Sort «Status»
   End If
   SetReady
   End If
   With mf_Tasks
   WindowState = 0
   Show
   ZOrder 0
   End With
   End Sub
   Здесь, несмотря на немногочисленность комментариев по именам вызываемых процедур, можно определить их назначение – таким образом, в некотором отношении этот код можно признать самодокументированным. С другой стороны, комментарии модульного уровня опять же отсутствуют – о том, что это ключевая подпрограмма с открытой областью действия, код ничего не говорит.

Как насчет связности и взаимозависимости?

   Что касается связности, то здесь вам придется поверить мне на слово, поскольку, как мы выяснили в предыдущем разделе, комментариев в коде не хватает и разобраться в общем процессе исполнения и структуре кода довольно сложно. Но, вставив комментарий насчет связности и взаимозависимости, я исправился. Вам же полагается знать следующее.
   Программа управляется объектом clsApplication с глобальной областью действия. Из него создаются и управляются на высоком уровне все остальные объекты. К примеру, в процедуре с понятным именем cLsAppLication.StartApplication создана родительская форма MDI и ряд других вспомогательных объектов. С точки зрения процесса исполнения программы это сугубо положительный момент.
   Объявления модулей в clsApplication выглядят так:
   Private WithEvents mf_Parent As nidiManager
   Private WithEvents mo_Projects As clsProjects
   Private WithEvents mo_Tasks As clsTasks
   Private WithEvents mo_Today As clsToday
   Private WithEvents mo_Archive As clsArchive
   Private mo_Contacts As clsContacts
   Private mo_DataService As clsDataService
   Private mo_Reports As clsReports
   Private mo_ProgConfig As clsProgConfig
   Private mo_PickList As clsPickList
   Private mc_Tasks As New Collection '–коллекция объектов clsTasks
   Private mc_Projects As New Collection '–коллекция объектов clsProjects
   Private mc_Sources As New Collection '–коллекция объектов clsSource
   Private moJJser As clsUser
   Private mo_Source As clsSource
   Private ms_DSN As String 'применяется при подключении
   Здесь, если не считать нехватки комментариев уровня модуля, присутствуют все дочерние объекты clsApplication. Из этого фрагмента кода в принципе можно вывести всю объектную иерархию программы.
   Реализация в программе многочисленных событий заметно «стройнит» код форм. В большинстве случаев родителями форм выступают модули классов, причем имена форм явственно свидетельствуют об этих отношениях. К примеру, clsProjects запускает frmProjects; впоследствии, если в форме происходит какое-то событие, его аналог сразу запускается в родительском элементе управления. Таким образом, код по большей части локализуется, что, в свою очередь, способствует инкапсуляции.
   Все содержащиеся в программе основные объекты (clsTasks, clsProjects и clsToday) обращаются к локальной копии объекта-источника данных под именем clsData Provider. Это модуль класса VB, в котором поведение источника данных приравнено к значению vbDataSource. Объект clsDataProvider пользуется услугами посредника clsDataService, причем посреднические обязанности осуществляются через функцию под именем GetDataProviderRS, которая одним своим именем демонстрирует факт извлечения набора записей источника данных. Здесь же расположено большинство управляющих программой SQL-операторов. Наконец, приставка «Get» указывает на действие и в некоторой степени объясняет назначение процедуры. По-моему, я уже достаточно пожурил свою живность насчет нехватки комментариев, так что об этом недостатке я больше говорить не буду.
   С точки зрения доступа к данным объект clsDataService является открытым интерфейсом логического звена данных. Объект clsDataProvider, описанный в предыдущем абзаце, обеспечивает взаимодействие между данными и уровнем графического пользовательского интерфейса. В составе clsDataService есть дочерний объект под именем clsDataAccess, который фактически осуществляет подключение к базе данных и исполняет передаваемые родительским объектом SQL-операторы. Подобного рода разделение обслуживания сводит взаимозависимость к минимуму и, по моему мнению, существенно облегчает сопровождение и модернизацию электронного администратора.

Другие достоинства и недостатки

   По существу, я лишь мельком коснулся некоторых аспектов, которые можно было бы проверить на этом примере. Далее я затрону еще пару вопросов, которые могут вам пригодиться при проверке собственного кода.
Как проводится подключение к базе данных?
   Соединение с базой данных устанавливается в начале исполнения, поддерживается в активном состоянии и по мере необходимости передается нуждающимся в данных объектам. Эта схема вполне приемлема для двухзвенной программы с одним пользователем. В то же время, если мы попытаемся масштабировать мою программу для нескольких пользователей, окажется, что обозначенное проектное решение не слишком удачно. Причина, по которой я к нему обратился, заключается в том, что его можно было удобно и быстро реализовать. Оправдывают ли эти преимущества принятые решения? В принципе, мне этого хватает, поскольку расширять свою программу я не собираюсь. С другой стороны, это наглядный пример принятия под давлением временных ограничений простейшего решения, о котором впоследствии можно сильно пожалеть.
Какую роль в моей программе исполняют наборы записей?
   Динамические наборы записей открываются в каждом объекте по отдельности и, подобно соединению с базой данных, поддерживаются в активном состоянии. Такое решение повышает реактивность программы, если с ней на одной физической машине работает один пользователь, но сильно ухудшает шансы на масштабирование. Бессвязные наборы записей в данном случае могли бы существенно улучшить возможности масштабирования программы.
   Нехватка связей между таблицами – это еще один очевидный недостаток кода, связанный с наборами записей. Иначе говоря, вместо того чтобы задать ссылку внешним ключом на таблицу проектов, я сохраняю имя проекта в таблице заданий. Таким образом, я полностью исключаю возможность переименования проектов.
    Плохой код и безмозглый кот!И, опять же, пожертвовав здесь значительно более оптимальным решением, я предпочел завершить кодирование побыстрее. Впрочем, в защиту выбранного решения выступает и то обстоятельство, что процесс генерации отчетов с помощью программы Crystal Reports при передаче в файл-шаблон ненормализированного набора записей значительно упростился. Кроме того, так мне стало удобнее разрабатывать сам шаблон – ведь, работая в программе Crystal Designer, я просто открывал базу данных и копировал в шаблон ее поля.
   Конечно, можно было бы подумать и над другими решениями, но то, на котором я остановился, по крайней мере, позволило оперативно генерировать для подведомственной мне группы еженедельные отчеты.
Есть ли в коде волшебные числа [141]?
   Нет. Поскольку мне хотелось сделать программу удобочитаемой, я в основном отдавал предпочтение закрытым и открытым перечислимым типам (перечислимые типы с глобальной областью действия присутствуют в basMain). Есть все же несколько массивов элементов управления, на которых вместо перечислимого типа установлена числовая ссылка. Но в основном, чтобы понять назначение параметра, достаточно взглянуть на его определение.

herdingcats@mindspring.com.
   Я вам советую иногда перечитывать главу 6 – так вы хотя бы не забудете основные принципы технического руководства. Написание этой главы, честно говоря, побудило меня сознаться в собственных неудачах в роли руководителя, и я в очередной раз осознал опасность неумения применять на практике полученные знания.

http://lib.rus.ec/b/110948 (прим. сост. FB2)
   • Weinberg, Gerald М. The Psychology of Computer Programming, Silver Anniversary Edition. New York: Dorset House Publishing, 1998.
   Еще одна классическая работа в нашей области. Вейнберг, до сих пор продолжающий активные исследования, этой своей книгой 1971 года перевернул представления о разработке программных средств. Он одним из первых провел углубленный анализ человеческого фактора и руководства в программировании, объединив тем самым технические аспекты процесса разработки с психологией. Многие принципы, изложенные в этом издании, на сегодняшний день не менее актуальны, чем тридцать лет назад.

http://lib.rus.ec/b/101297 (прим. сост. FB2)
   • Cockburn, Alistair. Agile Software Development. New York: Addison-Wesley, 2002.
   Термин «гибкая разработка» появился не так уж давно, но описанные в этой замечательной работе методы в полной мере адаптированы к реальным повседневным попыткам перенести бизнес-требования на реализацию программных продуктов. Если в ходе разработки область действия разрастается (а разве бывает по-другому?), прочтите эту книгу – она научит адекватно реагировать на такого рода изменения.
   Перевод на русский язык:  http://www.ozon.ru/context/detail/id/1315535/ (прим. сост. FB2)
   • DeMarco, Tom, and Timothy Lister. Peopleware: Productive Projects and Teams, Second Edition. New York: Dorset House Publishing, 1999.
   Переиздание любой книги по компьютерной тематике неизменно свидетельствует о ее качестве. Этот труд, написанный двумя профессиональными руководителями программных проектов, – лишнее тому подтверждение. В работе рассматриваются вопросы пространственного планирования, формирования рабочих групп, руководства специалистами и многие другие вопросы, способствующие упорядочиванию деятельности ведущих программистов.
   Русский перевод книги доступен на http://lib.rus.ec/b/72962 (прим. сост. FB2)
   • Highsmith, James A., III. Adaptive Software Development. New York: Dorset House Publishing, 2000.
   Будьте уверены – этой книге уготована долгая жизнь. Воспринимать изложенный в ней материал временами трудновато, но лишь потому, что Хайсмит заставляет читателя глубже анализировать характер процессов разработки программных средств. Подзаголовок книги гласит: «Сотрудничество в руководстве сложными системами». Автор логично доказывает сложность повседневной деятельности руководителей проектов и программистов. Из-за одних лишь его пассажей о сотрудничестве издание стоит приобрести. Работа замечательно дополняет труд Кокберна по той же теме.