| |||||
| |||||
После того как построена концептуальная модель, выявлены и задокументированы системные операции, можно перейти к этапу проектирования, на котором происходит распределение обязанностей между объектами предметной области.
Напомним, что этап анализа завершился выявлением по описаниям прецедентов набора системных событий и операций. Это то, что умеет делать система, кроме этих операций система больше не может ничего. Поэтому распределение обязанностей между объектами предметной области происходит для реализации системных операций.
Обязанность – это контракт или обязательство типа или класса. Можно выделить два типа обязанностей:
знание
а). наличие информации о данных
б). наличие информации о связанных объектах
в). наличие информации о вычисляемых величинах
действие
а). выполнение некоторых действий самим объектом
б). инициация действий других объектов
в). управление и координирование действий других объектов
Начинать распределение обязанностей следует с назначения некоторому понятию предметной области обязанности – выполнение системной операции. Кто должен отвечать за обработку системных событий? Ответить на этот вопрос помогает шаблон распределения обязанностей Controller, который гласит
Обязанности по обработке системных сообщений следует делегировать классу, удовлетворяющему одному из следующих условий:
Класс представляет всю систему в целом или ее подсистему (внешний контроллер).
Класс представляет всю организацию (внешний контроллер).
Класс представляет активный объект из реального мира (например, роль человека), который может участвовать в решении задачи (контроллер роли)
Класс представляет искусственный обработчик всех системных событий некоторого прецедента и обычно называется <имя прецедента>Handler (контроллер прецедента).
Замечание 1. Для всех системных событий в рамках одного прецедента используется один и тот же контроллер (таким образом, обеспечивается высокое зацепление операций внутри класса-контроллера(см. шаблон High Cohesion)).
Замечание 2. В перечень объектов-кандидатов на роль контроллера не входят объекты графического интерфейса пользователя. Такие объекты не выполняют задачи, связанные с системными событиями. Они обычно получают сообщения и делегируют их контроллерам – обусловлено трехуровневой архитектурой системы.
Пример. В файле library.rtmdl содержится концептуальная модель системы библиотека для реализации прецедента Выдача литературы на абонементе. Для данного прецедента можно выявить три системных операции:
GetChitFormulyar(number) – получить читательский формуляр по номеру читательского билета.
AddBook(number, data) – добавить информацию о книге в текущую книговыдачу по инвентарному номеру книги и сроку возврата.
EndKnigovyidacha() – завершить текущую книговыдачу.
Необходимо определится – какой из классов будет отвечать за выполнение этих системных операций. Согласно рассмотренному шаблону Контроллер – это один из классов Library, Abonement, Библиотекарь, KnigovyidachaHandler. Рассмотрим их все:
KnigovyidachaHandler – контроллер прецедента. В данной ситуации заводить искусственный объект не имеет смысла, поскольку число системных операций для этого прецедента мало. Контроллеры прецедентов вводятся, если число системных событий достигает нескольких десятков, либо число самих прецедентов очень велико.
Библиотекарь – контроллер роли. Контроллерами роли следует пользоваться очень осторожно, поскольку существует риск, что обязанности в системе будут распределены в соответствии с обязанностями человека. Но в каждой организации набор обязанностей, закрепленный за сотрудником свой, поэтому, если не предоставить никаких механизмов перераспределения обязанностей между сотрудниками, мы получим программу, написанную под одну организацию.
Library – внешний контроллер, представляющий всю организацию. В данном случае не подойдет, поскольку мы можем получить слишком раздутый контроллер, на который возложено слишком много обязанностей, которые слабо согласуются друг с другом (см. шаблон High Cohesion).
У нас остался только один кандидат – Abonement, на который и возложим обязанности по выполнению системных операций.
Теперь нам нужно выяснить как действует класс Abonement для выполнения своих обязанностей. Т.е. необходимо распределить обязанности между классами, с которыми связан Abonement. Что распределять – это выясняется из контракта системной операции, в частности из раздела постусловий.
Заводим CRC-карточку на Abonement и классы, имеющие с ним связь, следующего вида:
Имя класса
Ответственность
здесь перечисляется все, за что класс отвечает Сотрудничество
здесь указываются классы, к которым обращается данный, чтобы выполнить свою обязанность
Те классы, между которыми есть связь, соединяются между собой нитками. Теперь надо решить, какому из классов, связанных с Abonement нитками надо делегировать часть ответственности по выполнению системной операции. Еще раз напоминаю – что делать – мы определяем из контракта системной операции. Если мы решим, что какому-то классу все же следует передать сообщение, то мы его записываем в раздел Сотрудничество и переходим уже к CRC-карточке этого класса, при этом для него в разделе Ответственность записываем переданную ответственность и заводим CRC-карточку для каждого класса, связанного с ним, потом соединяем их нитью. Возникает вопрос: Как понять – какому из связанных классов и какую ответственность следует делегировать? Помочь ответить на этот вопрос должны два шаблона распределения обязанностей:
Шаблон Expert (Эксперт) говорит о том, что назначать обязанность надо информационному эксперту – классу, у которого имеется информация, требуемая для выполнения обязанности. Т.е., иными словами, объекты выполняют только те действия, которые связаны с имеющейся у них информацией. Проблема заключается в том, что для выполнения обязанности зачастую требуется информация, распределенная между различными объектами. Это предполагает, что должно существовать много “частичных” экспертов, взаимодействующих при выполнении общей задачи. Если информация распределена между различными объектами, то при выполнении общей задачи, они должны взаимодействовать друг с другом с помощью сообщений. То, насколько хорошо мы продумаем и определим структуру и количество этих сообщений, и определяет качество программного продукта.
Одним из специфичных наборов обязанностей являются обязанности по созданию объектов, распределение которых регламентирует шаблон Creator (Создатель): назначить классу В обязанность создавать экземпляры класса А, если выполняется одно из следующих условий: 1.1. Класс В агрегирует объекты А. 1.2. Класс В содержит объекты А. 1.3. Класс В записывает экземпляры объектов А. 1.4. Класс В активно использует объекты А. 1.5. Класс В обладает данными инициализации, которые будут передаваться объектам А при его создании. Согласно этому шаблону коллекция или класс-регистратор являются хорошими кандидатами на выполнение обязанностей, связанных с созданием объектов, которые они будут содержать или регистрировать. Исходя из этих описаний шаблонов видно, что не существует однозначного способа распределения обязанностей. Вопрос: при всем многообразии вариантов распределения обязанностей – какой будет лучше или хуже? В какой-то мере ответить на этот вопрос помогут следующие два шаблона распределения обязанностей: Шаблон Low Coupling (низкая степень связности). Степень связности – это мера того, насколько жестко один класс связан с другими классами, либо каким количеством информации о других классах он обладает. Класс с низкой степенью связанности зависит от не очень большого числа других классов. Выражение “очень много” зависит от контекста. Класс с высокой степенью связанности зависит от множества других классов. Однако наличие таких классов нежелательно, поскольку оно приводит к возникновению следующих проблем: 1.1. Изменения в связанных классах приводят к изменениям в данном классе. 1.2. Затрудняется понимание каждого класса в отдельности. 1.3. Усложняется повторное использование, поскольку для этого требуется дополнительный анализ классов с которыми связан данный. Шаблон гласит: обязанности между классами распределены хорошо, если степень связанности низкая. Другими словами, обязанности между классами следует распределять таким образом, чтобы для реализации каждой обязанности было задействовано как можно меньшее количество классов. Шаблон High Cohesion (Сильное зацепление). Зацепление – мера связанности и сфокусированности обязанностей класса. Считается, что класс обладает высокой степенью зацепления, если его обязанности тесно связаны друг с другом и он не выполняет работ непомерных объемов. Класс с низкой степенью зацепления выполняет много разнородных функций или несвязанных между собой обязанностей. Такие классы создавать нежелательно, поскольку они приводят к возникновению ряда существующих проблем: 1.4. Трудность в понимании. 1.5. Сложности при повторном использовании. 1.6. Сложности в поддержке. 1.7. Ненадежность, постоянная подверженность изменениям. Классы со слабым зацеплением, как правило, являются слишком “абстрактными” или выполняют обязанности, которые можно распределить между другими объектами. Шаблон гласит: распределять обязанности следует распределять таким образом, чтобы степень зацепления системы была высокой. Если выясняется, что после распределения обязанностей наши классы нарушают какой-то из последних шаблонов, то информацию с CRC-карточек стираем и перераспределяем обязанности заново. После того, как все текстовые описания обязанностей получены, по ним составляют диаграммы кооперации, по которым определяются методы каждого из классов предметной области. На этом стадия проектирования завершается. Практическая работа №1. Прецеденты и актеры Цели занятия: - Познакомиться с основными элементами рабочего интерфейса среды Rational Rose. - Получить навыки создания диаграммы прецедентов в среде Rational Rose. Некоторые понятия. Элементы экрана. Основными элементами интерфейса Rose являются браузер, окно документации, панели инструментов, окно диаграммы и журнал (log). Браузер (browser) Используется для быстрой навигации по модели. Окно документации (documentation window) Применяется для работы с документацей элементов модели. Панели инструментов (toolbars) Обеспечивают быстрый доступ к наиболее распространенным командам. Окно диаграммы (diagram window) Используется для просмотра и редактирования одной или нескольких диаграмм UML. Журнал (log) Применяется для просмотра ошибок и отчетов о результатах выполнения различных команд. Браузер. Браузер – это иерархическая структура, позволяющая легко осуществлять навигацию по модели. Все, что добавляется к модели: актеры, прецеденты, классы, компоненты – выводится в окне браузера. С помощью браузера можно: Добавлять к модели элементы. Просматривать существующие элементы модели. Перемещать элементы модели. Переименовывать элементы модели. Добавлять элементы модели к диаграмме. Группировать элементы в пакеты. Работать с описанием элемента. Открывать диаграмму. Четыре представления модели. Модель Rose поддерживает 4 представления (views): представление прецедентов (use case view), логическое представление (logical view), представление компонентов (component view) и представление размещения (deployment view). Представление прецедентов. Это представление включает в себя всех актеров, все прецеденты и их диаграммы для конкретной системы. Оно может также содержать диаграммы последовательности и кооперации. С остальными видами представлений мы познакомимся позже. Создание, открытие, удаление диаграмм прецедентов. Для получения доступа к Главной Диаграмме Прецедентов (Main Use Case Diagram): 1. В браузере щелкните мышью на значке “+” рядом с Представлением Прецедентов (Use Case View). Данное представление будет открыто. 2. Дважды щелкнув на Главной Диаграмме (Main), откройте ее. Чтобы создать новую Диаграмму Прецедентов (Use Case Diagram): Щелкните правой кнопкой мыши на Представлении Прецедентов (Use Case View) в браузере. Во всплывающем меню выберите пункт New > Use Case Diagram. Выделив новую диаграмму, введите ее имя. Дважды щелкнув на названии диаграммы в браузере, откройте ее. Чтобы удалить Диаграмму Прецедентов (Use Case Diagram): Щелкните правой кнопкой мыши на диаграмме в браузере. В появившемся меню выберите пункт Delete. Чтобы переименовать Диаграмму Прецедентов (Use Case Diagram): Щелкните правой кнопкой мыши на диаграмме в браузере. В появившемся меню выберите пункт Rename. Задание 1. Создайте диаграммы прецедентов “POST First”, “POST Second”. Удалите диаграмму прецедентов “POST Second”. Переименуйте Главную диаграмму Прецедентов (Main) на “POST”. Работа с Прецедентами (Use Case). Добавить Прецедент (Use Case) на Диаграмму Прецедентов (Use Case Diagram) можно следующим образом: 3. Выберите значок Прецедента (овал) на панели инструментов Представления Прецедентов (Use Case View). 4. Нажмите на Диаграмме Прецедентов (Use Case View) левой кнопкой мыши в том месте, где хотите разместить Прецедент (Use Case). или 1. Щелкните правой кнопкой мыши на пакете Представления Прецедентов(Use Case View) в браузере. 2. В открывшемся меню выберите пункт New > Use Case. 3. Новый вариант использования под названием NewUseCaseN новый вариант использования появится в браузере. 4. Выделив новый вариант использования, введите его название. 5. Чтобы поместить прецедент на диаграммму, перетщите его туда из браузера. Удаление Прецедентов (Use Case): Выделите Прецедент (Use Case) на диаграмме. Нажмите на клавишу Delete. Обратите внимание, что, хотя вариант использования удален с Диаграммы Прецедентов (Use Case Diagram), он остался в браузере и на других диаграммах системы. Для удаления Прецедента (Use Case) из модели: 1. Выделите Прецедент (Use Case) на диаграмме. 2. Выберите в меню пункт Edit > Delete From Model или нажмите Ctrl+D. или Щелкните правой кнопкой мыши на прецеденте (Use Case) в браузере. В появившемся меню выберите пункт Delete. При этом прецедент будет удален со всех диаграмм и из браузера. В Rose можно создать подробную спецификацию для каждого прецедента для документации его свойств. Открыть спецификацию прецедента можно следующим образом: Щелкните правой кнопкой мыши на Прецеденте (Use Case) в браузере или на Диаграме Прецедентов (Use Case Diagram). В появившемся меню выберите пункт Open specification. Назначение приоритета Прецеденту (Use Case) [используется для описания порядка реализации прецедентов]: Щелкните правой кнопкой мыши в браузере или на диаграмме прецедентов. В открывшемся меню выберите пункт Open Specification. На вкладке General введите приоритет в поле Rank. Добавить краткое описание прецедента (текст, отражающий суть происходящих процессов) можно следующим образом: 1. Щелкните правой кнопкой мыши в браузере или на диаграмме прецедентов. 2. В открывшемся меню выберите пункт Open Specification. 3. На вкладке General введите текст в поле Documentation. Или: 1. Открыть пункт меню View>Documentation. 2. Введите текст в окне документации. Связывание файлов с прецедентами (в файле, например, может содержаться описание прецедента в развернутом формате): Щелкните правой кнопкой мыши где-нибудь на свободном месте вкладки Files Окна Спецификации. В открывшемся меню выберите пункт Insert File. В диалоговом окне Open найдите и выделите требуемый файл. Нажмите кнопку Open для прикрепления к прецеденту указанного файла. Работа с актерами производится аналогично работе с прецедентами. Работа со связями. Связь ассоциации – на диаграммах прецедентов – это связь между актером и прецедентом. Чтобы создать связь такого типа необходимо: Нажмите кнопку Unidirectional Association панели инструментов. Проведите мышью, удерживая ее левую кнопку, от актера к прецеденту. Между актером и прецедентом будет показана стрелка, соответствующая связи. При этом направление стрелки показывает, кто инициирует взаимодействие. Связь включения: 5. Нажмите на кнопку Generalization (обобщение) панели инструментов. 6. Проведите мышью, удерживая левую кнопку, от объемлющего прецедента ко включаемому. 7. Между прецедентами будет нарисована связь обобщения. 8. Щелкните правой кнопкой мыши на линии связи и в открывшемся меню выберите пункт Open Specification. 9. Откроется окно спецификации обобщения. 10. В раскрывающемся списке Stereotype (стереотип) выберите или наберите заново пункт “include”. 11. Щелкнув на кнопке Ok, закройте окно спецификации. 12. Слово “include” появится рядом со стрелкой, соответствующей данному обобщению. если это слово не выводится, то щелкните правой кнопкой мыши на линии связи и в открывшемся меню пометьте пункт Stereotype Label. 13. Откройте окно спецификации включаемого варианта использования. 14. Установите флажок Abstract. Связь расширения задается аналогично связи включения, только со словом “extends”, обратным направлением стрелки, и помечается как абстрактный тот прецедент, от которого идет стрелка. На диаграмме прецедентов также можно размещать примечания. 3. Нажмите на панели инструментов кнопку Note. 4. Щелкните мышью в том месте диаграммы, где собираетесь поместить примечание. 5. Выделив новое примечание введите текст. Для прикрепления примечания к элементу диаграммы: Нажмите кнопку Anchor Note to Item панели инструментов. Нажав левую кнопку мыши, проведите ею от примечания к прецеденту. Задание 2. Создайте диаграмму прецедентов системы POST ( в окне главной диаграммы), как показано, при этом: Добавьте краткие описания всех прецедентов и актеров; Назначьте приоритеты прецедентам; Свяжите файл с прецедентом BuyItems, в котором дайте его описание в формате высокого уровня (в виде таблицы). Контрольные вопросы: 1. Что является основными элементами рабочего интерфейса среды Rational Rose? Какие функции выполняет браузер? 2. Какие четыре представления модели поддерживает Rose? 3. Что включает в себя представление прецедентов? 4. Для чего прецеденту назначается значение приоритета? 5. Какие существуют типы связей между прецедентами? Объясните их смысл. Практическая работа №6. Диаграммы компонентов Компонентом называется физический модуль кода. Компонентами бывают как библиотеки исходного программного кода, так и исполняемые файлы. Все компоненты изображают на диаграммах компонентов, где отражают связи между ними. Единственной связью между компонентами является отношение зависимости, которое показывает, что один из компонентов должен компилироваться до компиляции другого. Диаграммой компонентов называется диаграмма UML, на которой показаны компоненты системы и зависимости между ними. С помощью данной диаграммы можно выяснить какие файлы с программным кодом существуют и какие exe-файлы создаются в результате компиляции. Чтобы создать новую диаграмму компонентов необходимо выполнить ряд действий: В браузере щелкните правой кнопкой мыши на Component View (Представлении компонентов). В открывшемся меню выберите пункт NEW->Component Diagram. Введите имя новой диаграммы. Задание 1. Создайте диаграмму компонентов и назовите ее POST. После создания диаграммы необходимо поместить туда компоненты, при этом с каждым компонентом можно связать документацию, содержащую описание назначения компонента и его классов. Как и классы компоненты можно объединять в пакеты для лучшей организации. Как правило, для каждого пакета Логического представления системы создается один пакет представления компонентов. Чтобы добавить компонент на диаграмму компонентов, выполните следующие действия: Нажмите кнопку Component панели инструментов диаграммы. Щелкните мышью на диаграмме в том месте, куда нужно поместить новый компонент. Введите имя нового компонента. Определение деталей компонента. Как и для остальных элементов модели, у компонентов есть спецификация, в которой можно задать следующие элементы: Stereotype (Стереотип). Определяет назначение данного компонента. Существуют следующие виды стереотипов: 1.1. none – пакет общего назначения. 1.2. Subprogram Specification and Subprogram Body (Спецификация и тело модуля). Спецификация – это интерфейсная часть модуля, а тело реализация модуля, причем модуль не содержит в себе классов. 1.3. Main Program (Главная программа). Этот компонент содержит точку входа в программу (программу, которая будет выполняться первой). Иногда эти компоненты содержат класс, представляющий все приложение в целом. 1.4. Package Specification и Package Body (Спецификация и тело пакета). Пакет в данном случае – это реализация класса или набора классов. Спецификацией пакета является набор объявлений классов, а тело пакета содержит код операций класса. 1.5. DLL. Компонент являющийся динамической библиотекой. Documentation (документация) – описание назначения данного компонента. Кроме того с компонентом можно связать файл с документацией. Это можно сделать, например, так: Щелкните правой кнопкой мыши на компоненте в браузере или на диаграмме компонентов. В открывшемся меню выберите пункт Open Specification. Перейдите на вкладку Files (Файлы). Щелкните правой кнопкой мыши внутри белого поля этой вкладки. В открывшемся меню выберите пункт Insert File (Вставить файл). В диалоговом окне открытия файла укажите прикрепляемый файл. Для завершения процесса нажмите кнопку Open (Открыть). Добавление зависимостей между компонентами. Как уже отмечалось, единственный возможный тип связей между компонентами – это зависимость, означающая. что один компонент зависит от другого. зависимость между компонентами изображают пунктирной линией. Здесь компонент D зависит от компонента C. Это значит, что в компоненте D есть некоторый класс, зависящий от какого-то класса из компонента С. Зависимости имеют значение при компиляции. Так как D зависит от С, D не может быть скомпилирован до С. Следует избегать циклических зависимостей. Добавить зависимость можно следующим образом: Нажмите кнопку Dependency (Зависимость) панели инструментов. Проведите линию зависимости от клиента к серверу. Задание 2. Для каждого пакета логического представления создайте пакет модели компонентов. Для каждого класса (пусть A имя класса) из пакета определите два компонента, как показано на рисунке выше. Компонент с именем A содержит определение самого класса, а компонент с именем AImpl содержит код методов этого класса. Естественно, что код методов зависит от определения самих классов. После этого выясните отношения зависимости между компонентами, представляющими определения классов. Проследите, чтобы не было циклических зависимостей. Контрольные вопросы. Что такое компонент? Что такое зависимость между компонентами? Перечислите стереотипы компонентов. Практическая работа №2. “Структурное программирование. Использование стандартного компонента TList. Часть 1.” Tlist – это список указателей на любые объекты. Список – это динамическая структура данных, позволяющая хранить произвольное число последовательно расположенных друг за другом элементов. Компонент Tlist имеет следующие свойства и методы: Свойство Объявление/Описание Capacity property Capacity: Integer; Число указателей, которые могут храниться в объекте. Всегда не меньше Count. Count property Count: integer; Число указателей хранящихся в объекте. Items property Items[Index: Integer]: Pointer; Элементы массива указателей в объекте. List TPointerList = array[0..MasxListSize-1] of Pointer; PPointerList = ^TpointerList; property List: PpointerList; Указатель на массив указателей в объекте. Метод Объявление/Описание Add function Add(Item: Pointer): Integer; Добавление нового указателя Item в список. Clear procedure Clear; Очистка списка. Delete procedure Delete(Index: Integer); Удаление из списка элемента по его индексу. Destroy destructor Destroy; Уничтожения самого объекта список. Error class procedure Error(const Msg: string; Data: Integer); overload; class procedure Error(Msg: PResStringRec; Data: Integer); overload; Генерация исключения EListError с передачей ему параметров Msg (сообщение) и Data (число) Метод Объявление/Описание Exchange procedure Exchange(Index1, Index2: Integer); Взаимная перестановка двух элементов списка с индексами Index1, Index2. Expand function Expand: TList; Расширяет емкость списка (свойство Capacity) и копирует старый список в новый. First function First: Pointer; Возвращает первый указатель списка. IndexOf function IndexOf (Item: Pointer) Integer; Возвращает индекс первого вхождения в список заданного указателя Item. Insert procedure Insert(Index: Integer; Item: Pointer); Вставляет элемент Item в список в заданную позицию Index. Last function Last: Pointer; Возвращает последний указатель списка. Move procedure Move(CurIndex, NewIndex: Integer); Изменяет текущую позицию в списке CurIndex элемента в списке на NewIndex. Pack procedure Pack; Удаляет из списка указатели nil. Remove function Remove(Item: Pointer): Integer; Удаляет из списка элемент Item. Sort type TlistSortCompare = function (Item1, Item2: Pointer): Integer; procedure Sort(Compare: TListSortCompare); Сортирует список с использованием указанной функции сравнения Compare. Остальные методы наследуются от базового класса TObject, например, Free, ClassName, ClassNameIs. Пример. Изучение данного компонента будем проводить на основе следующей задачи: Хранение в памяти временной последовательности записей об учащихся, хранящих информацию о фамилии и годе рождения. Создайте проект в Delphi и назовите его DBStudents. Добавьте в проект модуль StudentsRecords и StudentsCatalog. Теперь необходимо понять: операции нам необходимо иметь для работы с набором записей и в каком виде хранить информацию. 3. Для хранения информации об одном учащемся создадим запись с полями Name и Year; добавьте следующие строки в раздел interface модуля StudentsRecords: type //раздел объявлений типов StudentsRec = record //определение записи Name: string; Year: Integer; end; TStudentsRec = ^StudentsRec; {определение типа – указатель на запись StudentsRec} Мы должны уметь создавать запись, удалять запись, редактировать. Реализуем эти возможности в виде набора функций и процедур: function CreateStudentsRecord(const Name: string; const Year: integer): TStudentsRec; procedure DestroyStudentsRecord(PStudentsRec: TStudentsRec); procedure SetStudentsName(PStudentsRec:TStudentsRec; const NewName: string); procedure SetStudentsYear(PStudentsRec:TStudentsRec; const NewYear: integer); function GetStudentsName(const PStudentsRec:TStudentsRec): string; function GetStudentsYear(const PStudentsRec:TStudentsRec): integer; 4. Добавьте эти четыре строки в раздел interface модуля StudentsRecords. Они будут вызываться клиентами данного модуля. Теперь необходимо эти 4 функции реализовать. С этой целью добавьте следующие строки в раздел implementation: function CreateStudentsRecord(const name: string; const year: integer): TStudentsRec; var PNewRec: TStudentsRec; begin New(PNewRec); //создаем динамически экземпляр записи PNewRec^.Name := name; //заполняем поля запси данными PNewRec^.Year := year; result := PNewRec; //возвращаем указатель на созданную запись end; procedure Destroy(PStudentsRec: TStudentsRec); begin Dispose(PStudentsRec); //освобождаем ранее выделенную память end; procedure SetStudentsName(PStudentsRec:TStudentsRec;const NewName: string); begin PStudentsRec^.Name := NewName; end; procedure SetStudentsYear(PStudentsRec:TStudentsRec; const NewYear: integer); begin PStudentsRec^.Year := NewYear; end; function GetStudentsName(const PStudentsRec:TStudentsRec): string; begin result := PStudentsRec^.Name; end; function GetStudentsYear(const PStudentsRec:TStudentsRec): integer; begin result := PStudentsRec^.Year; end; Теперь модуль StudentsRecords реализован полностью. Переходим к реализации модуля StudentsCatalog. Реализуем каталог студентов на основе списка TList. В каталог можно добавлять новую запись, изменять, удалять, сортировать записи, осуществлять поиск нужной записи, последовательно просматривать все записи, переходя к следующей, предыдущей, в начало и конец каталога. 5. Добавьте в интерфейсную часть модуля следующий текст: uses classes, //для работы с компонентом TList StudentsRecords; //для работы с записями type StudentsCatalogRec = record Impl: TList; //каталог студентов будет реализован на основе //компонента TList Current: integer; //индекс текущей записи end; TSudentsCatalog = ^StudentsCatalogRec; procedure StudentsCatalogCreate(cat: TStudentsCatalogRec); procedure StudentsCatalogDestroy(cat: TStudentsCatalogRec); function AddStudentsCatalogsRecord(cat: TStudentsCatalogRec; const pRec: TSudentsRec): integer; overload; function AddStudentsCatalogsRecord(cat: TStudentsCatalogRec; const name: string; const year:integer): integer; overload; procedure DeleteStudentsCatalogsRecord(cat: TStudentsCatalogRec; Index: integer); overload; procedure DeleteStudentsCatalogsRecord(cat: TStudentsCatalogRec; name: string; year:integer = -1); overload; function GetStudentsCatalogsRecord(cat: TStudentsCatalogRec; Index: integer): TStudentsRec; function FindStudentsCatalogsRecord(cat: TStudentsCatalogRec; name: string; year:integer = -1): TStudentsRec; procedure SetStudentsCatalogCurrentRecord(cat: TStudentsCatalogRec; Index: integer); procedure NextStudentsCatalogsRecord(cat: TstudentsCatalogRec); procedure PreviousStudentsCatalogsCurrentRecord(cat: TstudentsCatalogRec); procedure FirstStudentsCatalogsCurrentRecord(cat: TstudentsCatalogRec); procedure LastStudentsCatalogCurrentRecord(cat: TstudentsCatalogRec); 6. В разделе implementation необходимо написать реализацию этих функций. |