Методические указания по выполнению лабораторных и практических работ по мдк
Скачать 3.25 Mb.
|
Ход работы 1. Нарисовать в PlantUML диаграмму классов реализуемой программы. (проектирование) 2. Реализовать программу на Java. (реализация) Для каждого из шаблонов, предложенных в вариантах можно найти пример реализации UMLсхемы и кода в приложенной книге “Паттерны проектирования”. Также указана глава, где подробно описан данный шаблон. Шаблон “Стратегия”. Проект “Принтеры”. В проекте должны быть реализованы разные модели принтеров, которые выполняют разные виды печати. Шаблон “Наблюдатель”. Проект “Оповещение постов ГАИ”. В проекте должна быть реализована отправка сообщений всем постам ГАИ. Шаблон “Декоратор”. Проект “Универсальная электронная карта”. В проекте должна быть реализована универсальная электронная карта, в которой есть функции паспорта, страхового полиса, банковской карты и т. д. Практическая работа № 1.24. Использование структурных шаблонов Цель работы: познакомиться с понятием структурных шаблонов Теоретические сведения 98 Структура – это объединение одного либо более объектов (переменных, массивов, указателей, других структур). Как и массив, она представляет собой совокупность данных, но отличается от него тем, что к ее элементам необходимо обращаться по имени, и ее различные элементы не обязательно должны принадлежать одному типу. Структуры удобно использовать там, где разнообразные данные, относящиеся к одному и тому же объекту, необходимо объединять. Например, ученика средней школы характеризуют следующие данные: фамилия, имя, дата рождения, класс, возраст. Объявление структуры осуществляется с помощью ключевого слова struct, за которым следует ее тип, список элементов, заключенных в фигурные скобки. Ее можно представить в следующем общем виде: struct тип {тип элемента 1 имя элемента 1; тип элемента n имя элемента n; }; Именем элемента может быть любой идентификатор. В одной строке можно записывать через запятую несколько идентификаторов одного типа. Например: struct date { int day; int month; int year;} ; Русские буквы использовать в идентификаторе в языке СИ нельзя. Следом за фигурной скобкой, заканчивающей список элементов, могут записываться переменные данного типа, например: struct date {…} a, b, c; При этом выделяется соответствующая память. Выведенное имя типа можно использовать для объявления записи, например: struct date day;. Теперь переменная day имеет тип date. Разрешается вкладывать структуры одна на другую. Для лучшего восприятия структуры используем русские буквы в идентификаторах, в языке СИ этого делать нельзя. Задание 1: Написать программу, проанализировать результат struct УЧЕНИК { char Фамилия [15]; имя [15]; struct DATA ДАТА РОЖДЕНИЯ; int класс, возраст;}; Определенный выше тип DATA включает три элемента: День, Месяц, Год, содержащие целые значения (int). Запись УЧЕНИК включает элементы: ФАМИЛИЯ [15]; ИМЯ[15]; ДАТА РОЖДЕНИЯ, КЛАСС, ВОЗРАСТ. ФАМИЛИЯ [15] и ИМЯ [15] – это символьные массивы из 15 компонент каждый. Переменная ДАТА РОЖДЕНИЯ представлена составным элементом (вложенной структурой) ДАТА. Каждой дате рождения соответствуют день месяца, месяц и год. Элементы КЛАСС и ВОЗРАСТ содержат значения целого типа (int). После введения типов ДАТА и УЧЕНИК можно объявить переменные, значения которых принадлежат этим типам. Задание 2: Написать программу, проанализировать результат struct УЧЕНИК УЧЕНИКИ [50]; массив УЧЕНИКИ состоит из 50 элементов типа УЧЕНИК. В языке СИ разрешено использовать массивы структуры; записи могут состоять из массивов и других записей. Чтобы обратиться к отдельному компоненту структуры, необходимо указать ее имя, поставить точку и сразу за ней написать имя нужного элемента. Задание 3: Написать программу, проанализировать результат Ученики [1]. КЛАСС = 3; Ученики [1]. ДАТА РОЖДЕНИЯ. ДЕНЬ=5; Ученики [1]. ДАТА РОЖДЕНИЯ. МЕСЯЦ=4; Ученики [1]. ДАТА РОЖДЕНИЯ. ГОД=1979; Первая строка указывает, что 1-й ученик учится в третьем классе, а последующие строки – его дату рождения: 5.04.79. Каждый тип элемента структуры определяется соответствующей строкой объявления в фигурных скобках. Например, массив УЧЕНИКИ имеет тип УЧЕНИК, год является целым числом. Так как каждый элемент записи относится к определенному типу, его составное имя 99 может появляться везде, где разрешено использовать значение этого типа. Рассмотрим пример программы: /* Демонстрация записи */ #include < stdio.h > struct computer { int mem; int sp; char model [20]; }; /* Объявление записи типа computer, состоящей из трех элементов: mem, sp, model */ struct computer pibm = {512, 1, “ПЭВМЕС 1840.05”} /* Объявление и инициализация переменной pibm типа computer */ main ( ) { printf (“ персональная ЭВМ % s\n\n “, pibm.model); printf ( “объем оперативной памяти - % d К байт \n”, pibm.mem); printf (“производительность - % d млн. операций в секунду \n”, pibm.sp); /* вывод на экран значений элементов структуры */ } В данной программе объявляется запись computer, которая состоит из трех элементов: mem (память ЭВМ), sp (быстродействие), model [20] (модель ПЭВМ). Переменная pibm имеет тип computer и является глобальной. Строки pibm.model, pibm.mem, pibm. sp в операторе printf вызывают обращение к соответствующим элементам записи pibm типа computer, которым ранее были присвоены определенные значения. Результат работы программы имеет вид: персональная ЭВМ ПЭВМ ЕС 1840.05 объем оперативной памяти – 512 К байт производительность – 1 млн. операций в секунду Рассмотрим использование в программе вложенных структур: /* Демонстрация вложенных структур*/ # include < stdio.h > struct date { int day; int month; int year; }; /* Объявление записи типа date*/ struct person { char fam [20]; char im [20]; char ot [20]; struct date f1;}; /* Объявление структуры типа person; одним из элементов записи person является запись f1 типа date */ main ( ) { struct person ind1; /* обьявление переменной ind1 типа person */ printf ( “Укажите фамилию, имя, отчество, день, \n месяц” “ и год рождения гражданина ind1\n”); scanf (“ % S % S % S %d %d”, &ind1.fam, &ind1.im, &ind1.ot, & ind1.f1.day, &ind1.f1.month, &ind1.f1.year ); /* Ввод сведений о гражданине ind1 */ printf (“ Фамилия, имя, отчество: % S % S % S \n”, ind1.fam, ind1.im, ind1.ot); printf (“ Годрождения - % d \n”, ind1.f1.year); printf (“ Месяцрождения - % d -й \n”, ind1.f1.month); printf (“ День рождения - % d -й \n”, ind1.f1.day); /* Вывод сведений о гражданине ind1 */ } 100 Структура типа date ( дата) содержит три элемента: day (день), month (месяц), year (год). Структура типа person (человек) содержит четыре элемента: fam[20] (фамилия), im[20] (имя) , ot[20] (отчество), f1 (дата рождения). Последний из них (f1) – это вложенная запись типа date. Результаты работы программы: Укажите фамилию, имя, отчество, день, месяц и год рождения гражданина ind1 Алексеев Сергей Петрович 3 5 1978 Подчеркнутая информация вводится пользователем. Сведения о гражданине ind1 Фамилия, имя, отчество: Алексеев Сергей Петрович Год рождения – 1978 Месяц рождения – 5-й День рождения – 3-й В следующей программе рассмотрим использование структуры в виде элементов массива pibm. Каждый элемент состоит из следующих компонентов: mem (память), sp (объем винчестера), model [20] (модель ПЭВМ): /* Массивы записей */ #include < stdio.h > struct computer { int mem, sp; char model [20]; pibm [10];}; /* объявлениезаписитипа computer; объявление массива pibm типа computer */ main ( ) { int i, j, k, priz; for ( i=0; i<10; i++) { printf (“Введите сведения о ПЭВМ %d и признак (0-конец; \n другая цифра- продолжение)\n”, i); printf (“ модельПЭВМ - ”); scanf (“%S”, &pibm [i].model ); printf ( “объемоперативнойпамяти -”); scanf (“%d”, &pibm[i].mem); printf (“ объемвинчестера - ”); scanf ( “%d , &pibm[i].sp ”); printf (“признак - ”); scanf (“ %d ”, &priz ); k=i; if (!priz) break; } /* Здесь !priz – операцияотрицания priz; break – выходизцикла for, если priz=0 */ for (i=0; i<10, i++); { printf ( “\n О какой ПЭВМ Вы хотите получить сведения?\n (Введите номер от 0 до 9)\n” ); scanf ( “%d ”,&j ); if (j>k) { printf (“Нет сведений об этой ПЭВМ \n”); continue; } printf (“ персональнаяЭВМ %s\n ”, pibm[j].model); printf (“объем оперативной памяти - % d Мб \n ”, pibm[j].mem); printf (“объем винчестера - % d Мб \n ”, pibm[j].sp); printf (“ признак – “ ); scanf ( “ %d ”, &priz); 101 if (!priz) break; } /* Ввод сведений о ПЭВМ и занесение в массив pibm записей типа computer (первый цикл for); вывод на экран сведений о ПЭВМ (второй цикл for) */ } Результаты работы программы: Введите сведения о ПЭВМ и признак (0-конец; другая цифра – продолжение) модель ПЭВМ – АТ 486 SX объем оперативной памяти – 32 объем винчестера – 4 Гбайта признак – 1 Введите сведения о ПЭВМ и признак (0-конец; другая цифра – продолжение) модель ПЭВМ – АТ 386 DX объем оперативной памяти – 64 объем винчестера – 14 Гбайт признак – 0 О какой ПЭВМ Вы хотите получить сведения? (Введитн номер от 0 до 9) 1 модель ПЭВМ – АТ 386 DX объем оперативной памяти – 16 Мб объем винчестера – 2,5 Гбайт признак – 0 Практическая работа № 1.25. Использование поведенческих шаблонов Цель работы: изучить механизм работы поведенческих шаблонов Теоретический материал Поведенческие шаблоны — шаблоны проектирования, определяющие алгоритмы и способы реализации взаимодействия различных объектов и классов. Поведенческие шаблоны: 1. цепочка обязанностей (Chain of Responsibility); 2. команда (Command); 3. итератор (Iterator); 4. посредник (Mediator); 5. хранитель (Memento); 6. наблюдатель (Observer); 7. посетитель (Visitor); 8. стратегия (Strategy); 9. состояние (State); 10. шаблонный метод (Template Method). Цепочка обязанностей — поведенческий шаблон проектирования предназначенный для организации в системе уровней ответственности. Пример из жизни: например, у вас есть три платежных метода (A, B и C), настроенных на вашем банковском счёте. На каждом лежит разное количество денег. На A есть 100 долларов, на B есть 300 долларов и на C — 1000 долларов. Предпочтение отдается в следующем порядке: A, B и C. Вы пытаетесь заказать что-то, что стоит 210 долларов. Используя цепочку обязанностей, первым на возможность оплаты будет проверен метод А, и в случае успеха пройдет оплата и цепь разорвется. Если нет, то запрос перейдет к методу B для аналогичной проверки. Здесь A, B и C — это звенья цепи, а все явление — цепочка обязанностей. Простыми словами: цепочка обязанностей помогает строить цепочки объектов. Запрос входит с одного конца и проходит через каждый объект, пока не найдет подходящий обработчик. Ход работы Обратимся к коду. Приведем пример с банковскими счетами. Изначально у нас есть базовый Account с логикой для соединения счетов цепью и некоторые счета: abstract class Account { protected $successor; 102 protected $balance; public function setNext(Account $account) { $this->successor = $account; } public function pay(float $amountToPay) { if ($this->canPay($amountToPay)) { echo sprintf('Оплата %s, используя %s' . PHP_EOL, $amountToPay, get_called_class()); } elseif ($this->successor) { echo sprintf('Нельзя заплатить, используя %s. Обработка ..' . PHP_EOL, get_called_class()); $this->successor->pay($amountToPay); } else { throw new Exception('Ни на одном из аккаунтов нет необходимого количества денег'); } } public function canPay($amount): bool { return $this->balance >= $amount; } } class Bank extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Paypal extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Bitcoin extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } 103 } Теперь приготовим цепь, используя объявленные выше звенья (например, Bank, Paypal, Bitcoin): // Подготовим цепь // $bank->$paypal->$bitcoin // // Первый по приоритету банк // Если нельзя через банк, то Paypal // Если нельзя через Paypal, то Bitcoin $bank = new Bank(100); // Банк с балансом 100 $paypal = new Paypal(200); // Paypal с балансом 200 $bitcoin = new Bitcoin(300); // Bitcoin с балансом 300 $bank->setNext($paypal); $paypal->setNext($bitcoin); // Попробуем оплатить через банк $bank->pay(259); // Вывод // ============== // Нельзя заплатить, используя Банк. Обработка .. // Нельзя заплатить, используя Paypal. Обработка ..: // Оплата 259, используя Bitcoin! Примеры на Java и Python Команда (Command) Википедия гласит : Команда — поведенческий шаблон проектирования, используемый при объектно- ориентированном программировании, представляющий действие. Объект команды заключает в себе само действие и его параметры. Пример из жизни: Типичный пример: вы заказываете еду в ресторане. Вы (т.е. Client) просите официанта (например, Invoker) принести еду (то есть Command), а официант просто переправляет запрос шеф-повару (то есть Receiver), который знает, что и как готовить. Другим примером может быть то, что вы (Client) включаете (Command) телевизор (Receiver) с помощью пульта дистанционного управления (Invoker). Простыми словами: Позволяет вам инкапсулировать действия в объекты. Основная идея, стоящая за шаблоном — это предоставление средств, для разделения клиента и получателя. Обратимся к коду. Изначально у нас есть получатель Bulb, в котором есть реализация каждого действия, которое может быть выполнено: // Получатель class Bulb { public function turnOn() { echo "Лампочка загорелась"; } public function turnOff() { echo "Темнота!"; } } Затем у нас есть интерфейс Command, который каждая команда должна реализовывать, и затем у нас будет набор команд: interface Command 104 { public function execute(); public function undo(); public function redo(); } // Команда class TurnOn implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOn(); } public function undo() { $this->bulb->turnOff(); } public function redo() { $this->execute(); } } class TurnOff implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOff(); } public function undo() { $this->bulb->turnOn(); } public function redo() { $this->execute(); } } 105 Затем у нас есть Invoker, с которым клиент будет взаимодействовать для обработки любых команд: // Invoker class RemoteControl { public function submit(Command $command) { $command->execute(); } } Наконец, мы можем увидеть, как использовать нашего клиента: $bulb = new Bulb(); $turnOn = new TurnOn($bulb); $turnOff = new TurnOff($bulb); $remote = new RemoteControl(); $remote->submit($turnOn); // Лампочка загорелась! $remote->submit($turnOff); // Темнота! Шаблон команда может быть использован для реализации системы, основанной на транзакциях, где вы сохраняете историю команд, как только их выполняете. Если окончательная команда успешно выполнена, то все хорошо, иначе алгоритм просто перебирает историю и продолжает выполнять отмену для всех выполненных команд. Примеры на Java и Python Итератор (Iterator) Википедия гласит : Итератор — поведенческий шаблон проектирования. Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из агрегированных объектов. Пример из жизни: Старый радионабор будет хорошим предметом итератора, где пользователь может начать искать сигнал на каком-то канале и затем использовать кнопки переключения на следующий и предыдущий канал для перехода между соответствующими каналами. Или используем пример телевизора, где вы можете нажимать кнопки следующего или предыдущего канала для перехода через последовательные каналы, или, иными словами, они предоставляют интерфейс для итерирования между соответствующими каналами, песнями или радиостанциями. Простыми словами: Представляет способ доступа к элементам объекта без показа базового представления. Обратимся к примерам в коде. В PHP очень просто реализовать это, используя SPL (Standard PHP Library). Приводя наш пример с радиостанциями, изначально у нас есть Radiostation: class RadioStation { protected $frequency; public function __construct(float $frequency) { $this->frequency = $frequency; } public function getFrequency(): float { return $this->frequency; } } Затем у нас есть итератор: 106 use Countable; use Iterator; class StationList implements Countable, Iterator { /** @var RadioStation[] $stations */ protected $stations = []; /** @var int $counter */ protected $counter; public function addStation(RadioStation $station) { $this->stations[] = $station; } public function removeStation(RadioStation $toRemove) { $toRemoveFrequency = $toRemove->getFrequency(); $this->stations = array_filter($this->stations, function (RadioStation $station) use ($toRemoveFrequency) { return $station->getFrequency() !== $toRemoveFrequency; }); } public function count(): int { return count($this->stations); } public function current(): RadioStation { return $this->stations[$this->counter]; } public function key() { return $this->counter; } public function next() { $this->counter++; } public function rewind() { $this->counter = 0; } public function valid(): bool { return isset($this->stations[$this->counter]); } } Пример использования: 107 $stationList = new StationList(); // Добавление станций $stationList->addStation(new RadioStation(89)); $stationList->addStation(new RadioStation(101)); $stationList->addStation(new RadioStation(102)); $stationList->addStation(new RadioStation(103.2)); foreach($stationList as $station) { echo $station->getFrequency() . PHP_EOL; } $stationList->removeStation(new RadioStation(89)); // Удалит 89 станцию Примеры на Java и Python Посредник (Mediator) Википедия гласит : |