МЕТОДИЧЕСКИЕ РЕКОМЕНДАЦИИ ДЛЯ СТУДЕНТОВ ПРОФЕССИОНАЛЬНЫЙ ЦИКЛ Разработка программных модулей ЛЕКЦИИ на специальности СПО базовой подготовки 09.02.07 Информационные системы и программирование Содержание Введение ................................................................................................................... 4 Тема 1 Разработка программного обеспечения ................................................... 6 Понятие ЖЦ ПО ................................................................................................ 6 Компонентно-ориентированный подход при разработке ПО ...................... 6 Этапы разработки ПО. ...................................................................................... 8 Тема 2 Структурное программирование ............................................................. 11 Структурное программирование ................................................................... 11 Тема 3 Объектно-ориентированное программирование ................................... 13 Объектно-ориентированное программирование ......................................... 13 Тема 4 Событийно-управляемое программирование ........................................ 14 Основные принципы событийно-управляемого программирования ........ 14 Элементы управления..................................................................................... 16 Диалоговые окна. ............................................................................................ 17 Обработчики событий .................................................................................... 18 Введение в графику ........................................................................................ 19 Тема 5 Модульный принцип разработки ПО. .................................................... 22 Основные критерии оптимизации модулей ................................................. 22 Информационная закрытость. Связность. Виды связности ....................... 23 Сцепление. Типы сцепления. ......................................................................... 25 Тема 6 Основы работы с базами данных ............................................................ 26 Специальные библиотеки. ............................................................................. 26 Базовый синтаксис SQL. ................................................................................ 27 Создание таблицы, работа с данными. ......................................................... 29 Python DB-API модули ................................................................................... 31 Объектно-реляционное отображение (ORM)............................................... 33 Тема 7 Разработка пользовательского интерфейса ........................................... 35 Правила разработки интерфейсов пользователя ......................................... 35 Требования к интерфейсу .............................................................................. 36 Анализ интерфейса ......................................................................................... 38 Тема 8 Архитектурные шаблоны в проектировании ......................................... 40 Назначение и виды паттернов ....................................................................... 40 2 Порождающие паттерны Abstract Factory, Builder, Factory method ........... 41 Порождающие паттерны: Prototype, Singleton ............................................. 43 Структурные паттерны: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy ............................................................................................... 44 Поведенческие паттерны: Iterator, Observer, State, Strategy, Visitor, Template method .............................................................................................. 46 Поведенческие паттерны: Chain of Responsibility, Memento, Command, Mediator ............................................................................................................ 47 Тема 9 Конструирование ПО ............................................................................... 49 Рефакторинг. Определение, причины и цели. Приемы рефакторинга ...... 49 Составление методов. ..................................................................................... 50 Перемещение функций между объектами ................................................... 51 Организация данных. ...................................................................................... 53 Упрощение условных выражений ................................................................. 56 Упрощение вызовов методов. ........................................................................ 57 Решение задач обобщения ............................................................................. 60 Экстремальное программирование ............................................................... 62 Тестирование. Виды, задачи .......................................................................... 62 Модульные тесты. Интеграционные тесты. ................................................. 64 Регрессионное тестирование. Нагрузочное тестирование. ........................ 65 Стандарты оформления кода ......................................................................... 68 Тема 10 Проведение изменений в ПО ................................................................. 70 Обратный инжиниринг. Определение, цели проведения ........................... 70 Этапы обратного инжиниринга ..................................................................... 71 Анализ исходных данных .............................................................................. 73 Получение экспертиз. Реверсивный инжиниринг ....................................... 74 Методики проведения обратного инжиниринга .......................................... 76 Оформление кода ............................................................................................ 77 Реинжиниринг ................................................................................................. 79 Анализ интерфейса ......................................................................................... 80 Список использованных источников .................................................................. 83 3 Введение Программная инженерия является отраслью информатики, которая изучает вопросы закономерности построения развития компьютерных программирования, программ, отражает обобщает опыт программирования в виде комплекса знаний и правил регламентации инженерной деятельности разработчиков ПС (программных средств). Международным комитетом при американском объединении компьютерных специалистов ACM (Association for Computing Machinery) и институте инженеров по электронике и электротехнике IEEE было создано ядро знаний SWEBOK (Software Engineering Body of Knowledge) (2001, 2003 гг.). В этом ядре были систематизированы разнородные знания в области программирования, планирования и управления, сформулировано понятие программной инженерии и областей, которые соответствуют процессам проектирования ПС и методам их поддержки. Программная инженерия охватывает все аспекты создания ПС, начиная от формирования требований до создания, сопровождения и снятия с эксплуатации, а также включает инженерные методы оценки трудозатрат, стоимости, производительности и качества. Кроме программистов, занимающихся непосредственно разработкой ПС, в программной инженерии задействованы: отслеживают сроки его исполнения и затраты; создают для проекта модель жизненного цикла ПС, удовлетворяющую его целям и задачам; 4 т правильность выполнения процесса разработки ПС путем тестирования, и на основе собранных данных проводят измерения характеристик качества; требованиям (в технике валидация подтверждает, что требования пользователя удовлетворены); алгоритмов и программ в проекте путем их сопоставления с эталонными данными, алгоритмами или программами (верификация — это внутренний процесс управления качеством, обеспечивающий согласование с правилами или спецификацией). 5 Тема 1 Разработка программного обеспечения Понятие ЖЦ ПО Создание любого программного средства выполняется по некоторой схеме. Данная схема представляет собой последовательность стандартных этапов производственного процесса. Этот процесс нужно спланировать, оценить его ресурсы. В ходе реализации этого процесса нужно: спроектировать ПС в виде системы, состоящей из компонент; описать функции этих компонент и их связи между собой; запрограммировать эти компоненты, автономно их отладить, собрать вместе и провести комплексную отладку; подготовить документацию на ПС; обучить пользователей; провести опытную эксплуатацию ПС; организовать сопровождение системы. Для сокращения затрат необходимо конкретизировать схему, упорядочить действия, выполняемые на каждом этапе, разработать методы решения возникающих на разных этапах проблем. В довершение ко всему, схема подразумевает возвраты назад (циклы), в тех случаях, когда обнаруживаются ошибки предыдущего этапа. Под жизненным циклом программного средства (ЖЦПС) понимают весь период его разработки и эксплуатации, начиная от момента возникновения замысла ПС и кончая прекращением его использования. В настоящее время можно выделить пять основных подходов к организации процесса создания и использования ПС. Компонентно-ориентированный подход при разработке ПО ООП-подход в разработке ПО хорошо себя зарекомендовал. Инкапсуляция, наследование и полиморфизм помогают программисту повторно использовать функциональность базового класса и скрыть детали реализации. Но при разработке сложных систем, особенно систем, моделирующих процессы окружающего 6 мира, ООП-программист сталкивается с рядом новых проблем, для решения которых необходим принципиально иной подход. Главное отличие компонента от объекта заключается в том, что компонент функционирует в среде как часть единой системы – «фреймворка». Фреймворк предоставляет унифицированный способ доступа к ресурсам среды в рамках компонентной модели. Под средой (или окружением) обычно понимается среда выполнения — вычислительное окружение, необходимое для выполнения программы. В конечном итоге компонент взаимодействует как с операционной системой/виртуальной машиной, так и с пользователем, БД, периферийными устройствами и прочими объектами реального мира, используя возможности фреймворка. В контексте жизненного цикла разработки ПО, под средой также может пониматься среда разработки, среда выполнения и т.п. Компонент может рассматриваться как объект, к которому предъявлен ряд дополнительных требований для функционирования в сложных средах с элементами неопределенности. Базовый класс компонента должен содержать всю необходимую функциональность, которая позволяет называть компонент компонентом. Если жизненный цикл объекта в ООП, как правило, известен заранее вплоть до мельчайших деталей еще на этапе разработки архитектуры системы, то компоненты, являющиеся частью сложной системы, могут инициировать взаимодействие друг с другом в различное время «по требованию». При этом они изначально могут ничего не знать друг о друге — на этапе написания компонента программист ограничен возможностями компонентной модели. Поэтому компоненты должны уметь динамически «обрабатывать» события и обмениваться информацией о своих свойствах и умениях с другими компонентами. Вот некоторые особенности, в той или иной мере присущие компонентноориентированному программированию: интроспективность (способность компонентов к самоописанию), 7 модульность и разграничение уровней доступа, автоматическая обработка исключительных ситуаций, автоматическое управление памятью, позднее связывание и динамический контроль типов, обработка событий, персистентность (способность компонентов сохранять и восстанавливать состояние), простота повторного использования. Компоненты могут представлять сущности моделируемой системы, ресурсы среды (предоставлять доступ к файловой системе, базам данных), управлять рабочим потоком, предназначаться для выполнения определенных задач. Этапы разработки ПО. Разработка любой программы состоит из нескольких этапов, грамотная реализация которых является обязательным условием для получения хорошего результата. Анализ требований В рамках взаимодействие этой стадии нуждающегося происходит в максимально программном решении эффективное клиента и сотрудников компании-разработчика, в ходе обсуждения деталей проекта помогающих более четко сформулировать предъявляемые к ПО требования. Результатом проведенного анализа становится формирование основного регламента, на который будет опираться исполнитель в своей работе — технического задания на разработку программного обеспечения. ТЗ (техническое задание) должно полностью описывать поставленные перед разработчиком задачи и охарактеризовать конечную цель проекта в понимании заказчика. Проектирование 8 Качественный анализ перспектив и возможностей создаваемого продукта станет основой для его полноценного функционирования и выполнения всего комплекса возлагаемых на ПО задач. Одной из составных частей этапа проектирования, к примеру, является выбор инструментальных средств и операционной системы, которых сегодня на рынке присутствует очень большое количество. В рамках данного этапа стороны должны осуществить: оценку результатов проведенного первоначально анализа и выявленных ограничений; поиск критических участков проекта; формирование окончательной архитектуры создаваемой системы; анализ необходимости использования программных модулей или готовых решений сторонних разработчиков; проектирование основных элементов продукта — модели базы данных, процессов и кода; выбор среды программирование и инструментов разработки, утверждение интерфейса программы, включая элементы графического отображения данных; определение основных требований к безопасности разрабатываемого ПО. Кодирование Кодирование может происходить параллельно со следующим этапом разработки — тестированием программного обеспечения, что помогает вносить изменения непосредственно по ходу написания кода. Уровень и эффективность взаимодействия всех элементов, задействованных для выполнения сформулированных задач компанией-разработчиком, на текущем этапе является самым важным — от слаженности действий программистов, тестировщиков и проектировщиков зависит качество реализации проекта. Тестирование и отладка Процесс тестирования позволяет смоделировать ситуации, при которых программный продукт перестает функционировать. Отдел отладки затем 9 локализует и исправляет обнаруженные ошибки кода, «вылизывая» его до практически идеального состояния. Эти два этапа занимают не меньше 30% затрачиваемого на весь проект времени, так как от их качественного исполнения зависит судьба созданного силами программистов программного обеспечения. Нередко функции тестировщика и отладчика исполняет один отдел, однако самым оптимальным будет распределить эти обязанности между разными исполнителями, что позволит увеличить эффективность поиска имеющихся в программном коде ошибок. 10 Тема 2 Структурное программирование Структурное программирование Алгоритмизация - это представление неформального, неточного и неполного описания известного метода решения задачи в виде четкого алгоритма. Это - не простая проблема. Систематические методы алгоритмизации появились лишь в начале 70-х годов и связаны, прежде всего, с двумя независимыми друг от друга идеями: структурное программирование и разработка сверху вниз. Эти идеи произвели настоящую революцию в программировании и способствовали его индустриализации. Они лежат в основе современной технологии программирования. В принципе, обе идеи достаточно просты и используются не только в программировании. Структурное программирование - это метод программирования, в котором используются только алгоритмы, построенные из стандартного набора базовых структур (так называемые структурные алгоритмы). Идея структурного программирования - стандартизация для борьбы с ошибками: ограничить возможную структуру алгоритмов, сделав их более простыми и наглядными. При этом облегчается понимание, разработка, изменение, отладка и верификация алгоритма, уменьшается количество возможных ошибок, упрощается их поиск и, в конечном счете, увеличивается производительность труда программистов и повышается качество программ. В частности, повышаются их надежность и мобильность, упрощается модернизация. При этом, правда, алгоритм может стать более громоздким. Как и любая стандартизация, структурное программирование рассчитано, прежде всего, на индустриальный подход – коллективную разработку больших и сложных программ в промышленных масштабах. Его роль повышается с ростом размеров и сложности разрабатываемых программ. 11 Алгоритм называется структурным (иногда говорят "структурированным"), если он имеет одну из базовых структур. Каждый блок этих структур сам может иметь внутри любую из этих допустимых структур и т.д. Таким образом из базовых структур можно построить структурный алгоритм любой сложности. В качестве базовых структур обычно используют последовательность, ветвление и цикл. 12 Тема 3 Объектно-ориентированное программирование Объектно-ориентированное программирование Объектно-ориентированное программирование (ООП) — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определённого класса, а классы образуют иерархию наследования. Идеологически ООП — подход к программированию как к моделированию информационных объектов, решающий на новом уровне основную задачу структурного программирования: структурирование информации с точки зрения управляемости, что существенно улучшает управляемость самим процессом моделирования, что, в свою очередь, особенно важно при реализации крупных проектов. Управляемость для иерархических систем предполагает минимизацию избыточности данных (аналогичную нормализации) и их целостность, поэтому созданное удобно управляемым — будет и удобно пониматься. Таким образом, через тактическую задачу управляемости решается стратегическая задача — транслировать понимание задачи программистом в наиболее удобную для дальнейшего использования форму. Основные принципы структурирования в случае ООП связаны с различными аспектами базового понимания предметной задачи, которое требуется для оптимального управления соответствующей моделью: абстракция для выделения в моделируемом предмете важного для решения конкретной задачи по предмету, в конечном счёте — контекстное понимание предмета, формализуемое в виде класса; инкапсуляция для быстрой и безопасной организации собственно иерархической управляемости: чтобы было достаточно простой команды «что делать», без одновременного уточнения как именно делать, так как это уже другой уровень управления; 13 наследование для быстрой и безопасной организации родственных понятий: чтобы было достаточно на каждом иерархическом шаге учитывать только изменения, не дублируя всё остальное, учтённое на предыдущих шагах; полиморфизм для определения точки, в которой единое управление лучше распараллелить или наоборот — собрать воедино. То есть фактически речь идёт о прогрессирующей организации информации согласно «важное/неважное», первичным семантическим «ключевое/подробности», критериям: «родительское/дочернее», «единое/множественное». Прогрессирование, в частности, на последнем этапе даёт возможность перехода на следующий уровень детализации, что замыкает общий процесс. Тема 4 Событийно-управляемое программирование Основные принципы событийно-управляемого программирования Событийно-ориентиированное программиирование (англ. eventdriven programming; в дальнейшем СОП) — парадигма программирования, в которой выполнение программы определяется событиями — действиями пользователя (клавиатура, мышь, сенсорный экран), сообщениями других программ и потоков, событиями операционной системы (например, поступлением сетевого пакета). СОП можно также определить как способ построения компьютерной программы, при котором в коде (как правило, в головной функции программы) явным образом выделяется главный цикл приложения, тело которого состоит из двух частей: выборки события и обработки события. Как правило, в реальных задачах оказывается недопустимым длительное выполнение обработчика события, поскольку при этом программа не может 14 реагировать на другие события. В связи с этим при написании событийноориентированных программ часто применяют автоматное программирование. Под событием в языке программирования обычно понимается способ внедрения того или иного фрагмента в программный код с целью изменения поведения программы. Как только происходит изменение среды вычислений из числа представляющих интерес для разработчика или пользователя программного обеспечения, активизируется событие и выполняется соответствующий фрагмент кода. В целом, с точки зрения практического программирования, обработка события подобна вызову процедуры, причем в роли параметров выступают те или иные характеристики среды вычислений. Любой современный интерфейс пользователя (или, в математической терминологии, среда вычислений) построен на основе обработки событий (onClick, onMouseMove, onMouseOver и т.д.). События, которые осуществляют взаимодействие с каналами локальных сетей, операционной системой, сторонними приложениями и т.д. могут также активизироваться по времени. В соответствии со схемой двухуровневой концептуализации, первый уровень может означать, например, инициацию пользователем события "щелчок левой кнопкой мыши", а второй - изменение состояния объекта меню при выборе соответствующего пункта меню. Как видим, сначала возможный индивид становится действительным (т.е. происходит активация события), а затем осуществляется означивание объекта программы (изменяется текущая позиция меню). Фрагментом кода программы в таком случае является метод, изменяющий текущую позицию меню, который активизируется, исходя из значения первого соотнесения (т.е. конкретизации события). Таким образом, на основе механизма событий осуществляется управление программой. 15 Элементы управления Почти все современные графические интерфейсы общего назначения строятся по модели WIMP - Window, Icon, Menu, Pointer (окно, иконка, меню, указатель). Внутри окон рисуются элементы графического интерфейса, которые для краткости будут называться виджетами (widget - штучка). Меню могут располагаться в различных частях окна, но их поведение достаточно однотипно: они служат для выбора действия из набора предопределенных действий. Программа, реализующая графический интерфейс, событийно ориентирована. Она ждет от интерфейса событий, которые и обрабатывает сообразно своему внутреннему состоянию. Эти события возникают в элементах графического интерфейса (виджетах) и обрабатываются прикрепленными к этим виджетам обработчиками. Сами виджеты имеют многочисленные свойства (цвет, размер, расположение), выстраиваются в иерархию принадлежности (один виджет может быть хозяином другого), имеют методы для доступа к своему состоянию. Расположением виджетов (внутри других виджетов) ведают менеджеры расположения. Виджет устанавливается на место по правилам менеджера расположения. В Tk имеются три типа менеджеров расположения: простой упаковщик (pack), сетка (grid) и произвольное расположение (place). Рассмотрим некоторые классы виджетов библиотеки Tk : 1. Button (Кнопка) Простая кнопка для вызова некоторых действий 2. Canvas (Рисунок) Основа для вывода графических примитивов. 3. Checkbutton (Флажок) Кнопка, которая умеет переключаться между двумя состояниями при нажатии на нее. 4. Entry (Поле ввода) Поле, в которое можно ввести строку текста. 5. Frame (Рамка) Виджет, который содержит в себе другие визуальные компоненты. 6. Label (Надпись) Виджет может показывать текст или изображение. 16 7. Listbox (Список) Прямоугольная рамка со списком, из которого пользователь может выделить один или несколько элементов. 8. Menu (Меню) Элемент, с помощью которого можно создавать всплывающие (popup) и ниспадающие (pulldown) меню. 9. Radiobutton (Селекторная кнопка) Кнопка для представления одного из альтернативных значений. Такие кнопки, как правило, действует в группе. 10. Scrollbar (Полоса прокрутки) Полоса прокрутки служит для отображения величины прокрутки в других виджетах. 11. Text (Форматированный текст) Этот прямоугольный виджет позволяет редактировать и форматировать текст с использованием различных стилей. 15. Toplevel (Окно верхнего уровня) Показывается как отдельное окно и содержит внутри другие виджеты. Диалоговые окна. Пакет tkinter содержит несколько модулей, предоставляющих доступ к уже готовым диалоговым окнам. Это окна различных сообщений, выбора по принципу "да-нет", открытия и сохранения файлов и др. Рассмотрим примеры окон из модулей messagebox и filedialog пакета tkinter. Модули пакета необходимо импортировать отдельно. То есть вы импортируете содержимое tkinter (например, from tkinter import *) и отдельно входящий в состав пакета tkinter модуль. Способы импорта на примере messagebox и пример вызова одной из функций модуля: import tkinter.messagebox → tkinter.messagebox.askyesno() from tkinter.messagebox import * → askyesno() from tkinter import messagebox → messagebox.askyesno() from tkinter import messagebox as mb (вместо mb может быть любой идентификатор) → mb.askyesno() 17 Окно выбора "да" или "нет" – askyesno: Нажатие "Да" в диалоговом окне возвращает в программу True, "Нет" вернет False (также как закрытие окна через крестик). Таким образом в коде можно обработать выбор пользователя. В данном случае если последний соглашается, то данные переносятся из поля в метку. Опции title и message являются позиционными, так что можно указывать только значения: askyesno("Вопрос", "Перенести данные?"). Подобные окна генерируются при использовании функции askokcancel с надписями на кнопках "ОК" и "Отмена", askquestion (возвращает не True или False, а строки 'yes' или 'no'), askretrycancel ("Повторить", "Отмена"), askyesnocancel ("Да", "Нет", "Отмена"). Другую группу составляют окна с одной кнопкой, которые служат для вывода сообщений различного характера. Это showerror, showinfo и showwarning. Рассмотрим две функции из модуля filedialog – askopenfilename и asksaveasfilename. Первая предоставляет диалоговое окно для открытия файла, вторая – для сохранения. Обе возвращают имя файла, который должен быть открыт или сохранен, но сами они его не открывают и не сохраняют. Делать это уже надо программными средствами самого Python. Опция filetype позволяет перечислить типы файлов, которые будут сохраняться или открываться, и их расширения. Обработчики событий В tkinter с помощью метода bind между собой связываются виджет, событие и действие. Например, виджет – кнопка, событие – клик по ней левой кнопкой мыши, действие – отправка сообщения. Другой пример: виджет – текстовое поле, событие – нажатие Enter, действие – получение текста из поля методом get для последующей обработки программой. Действие оформляют как функцию или метод, которые вызываются при наступлении события. 18 При вызове метода bind событие передается в качестве первого аргумента. Название события заключается в кавычки, а также в угловые скобки < и >. События описывается с помощью зарезервированных ключевых слов. Часто используемые события, производимые мышью: <Button-1> – клик левой кнопкой мыши <Button-2> – клик средней кнопкой мыши <Button-3> – клик правой кнопкой мыши <Double-Button-1> – двойной клик левой кнопкой мыши <Motion> – движение мыши и т. д. Событие (Event) – это один из объектов tkinter. У событий есть атрибуты, как и у многих других объектов. При использовании функции move можно извлечь значения атрибутов x и y объекта event, в которых хранятся координаты местоположения курсора мыши в пределах виджета, по отношению к которому было сгенерировано событие. Для событий с клавиатуры буквенные клавиши можно записывать без угловых скобок (например, 'a'). Для неалфавитных клавиш существуют специальные зарезервированные слова. Например, <Return> - нажатие клавиши Enter, <space> - пробел. (Заметим, что есть событие <Enter>, которое не имеет отношения к нажатию клавиши Enter, а происходит, когда курсор заходит в пределы виджета.) Введение в графику В tkinter от класса Canvas создаются объекты-холсты, на которых можно "рисовать", размещая различные фигуры и объекты. Делается это с помощью вызовов соответствующих методов. При создании экземпляра Canvas необходимо указать его ширину и высоту. При размещении геометрических 19 примитивов и других объектов указываются их координаты на холсте. Точкой отсчета является верхний левый угол. Сначала в программе ниже создается холст. c = Canvas (width=200, height=200, bg='white') На нем с помощью соответствующих методов размещаются примитивы. Метод create_line рисует отрезки. Сначала указываются координаты начала (x1, y1), затем – конца (x2, y2). c.create_line(10, 10, 190, 50) c.create_line(100, 180, 100, 60, fill='green', width=5, activefill='lightgreen') Свойство activefill определяет цвет отрезка при наведении на него курсора мыши. Создание прямоугольников методом create_rectangle: c.create_rectangle(10, 10, 190, 60) c.create_rectangle(60, 80, 140, 190, fill='yellow', outline='green', width=3) Первые координаты – верхний левый угол, вторые – правый нижний. Методом create_polygon рисуется произвольный многоугольник путем задания координат каждой его точки: c.create_polygon(100, 10, 20, 90, 180, 90) c.create_polygon(40, 110, 160, 110, 190, 180, 10, 180, fill='orange') Для удобства координаты точек можно заключать в скобки: c.create_polygon((40, 110), (160, 110), (190, 180), (10, 180), fill='orange') Метод create_oval создает эллипсы. При этом задаются координаты гипотетического прямоугольника, описывающего эллипс. Если нужно получить круг, то соответственно описываемый прямоугольник должен быть квадратом. c.create_oval(50, 10, 150, 110, width=2) c.create_oval(10, 120, 190, 190, fill='grey70', outline='white') Более сложные для понимания фигуры получаются при использовании метода create_arc. В зависимости от значения опции style можно получить сектор (по умолчанию), сегмент (CHORD) или дугу (ARC). Также как в случае 20 create_oval координаты задают прямоугольник, в который вписана окружность (или эллипс), из которой "вырезают" сектор, сегмент или дугу. Опции start присваивается градус начала фигуры, extent определяет угол поворота. c.create_oval(10, 10, 190, 190, fill='lightgrey', outline='white') c.create_arc(10, 10, 190, 190, start=0, extent=45, fill='red') c.create_arc(10, 10, 190, 190, start=180, extent=25, fill='orange') c.create_arc(10, 10, 190, 190, start=240, extent=100, style=CHORD, fill='green') c.create_arc(10, 10, 190, 190, start=160, extent=-70, style=ARC, utline='darkblue', width=5) 21 Тема 5 Модульный принцип разработки ПО. Основные критерии оптимизации модулей Оптимизация программного кода — это модификация программ, выполняемая оптимизирующим компилятором или интерпретатором с целью улучшения их характеристик, таких как производительности или компактности, — без изменения функциональности. Поэтому возникает две задачи. Первая задача: подготовить такой набор тестов и применить к ним ПС, чтобы обнаружить в нем по возможности большее число ошибок. Однако чем дольше продолжается процесс тестирования (и отладки в целом), тем большей становится стоимость ПС. Отсюда вторая задача: определить момент окончания отладки ПС (или отдельной его компоненты). Признаком возможности окончания отладки является полнота охвата пропущенными через ПС тестами (т.е. тестами, к которым применено ПС) множества различных ситуаций, возникающих при выполнении программ ПС, и относительно редкое проявление ошибок в ПС на последнем отрезке процесса тестирования. Для каждого модуля, подвергаемого тестированию, разрабатывается тестовое окружение, включающее в себя драйвер и заглушки, готовятся тесттребования и тест-планы, описывающие конкретные тестовые примеры. Основная цель модульного тестирования – удостовериться в соответствии требованиям каждого отдельного модуля системы перед тем, как будет произведена его интеграция в состав системы. При этом в ходе модульного тестирования решаются четыре основные задачи. 1. Поиск и документирование несоответствий требованиям – это классическая задача тестирования, включающая в себя не только разработку тестового окружения и тестовых примеров, но и выполнение тестов, протоколирование результатов выполнения, составление отчетов о проблемах. 2. Поддержка разработки и рефакторинга низкоуровневой архитектуры системы и межмодульного взаимодействия. Даже при классической схеме 22 тестирования модульные тесты могут выявить проблемы в дизайне системы и нелогичные или запутанные механизмы работы с модулем. 3. Поддержка рефакторинга модулей – эта задача связана с поддержкой процесса изменения системы. Достаточно часто в ходе разработки требуется проводить рефакторинг модулей или их групп – оптимизацию или полную переделку программного кода с целью повышения его сопровождаемости, скорости работы или надежности. 4. Поддержка устранения дефектов и отладки — эта задача сопряжена с обратной связью, которую получают разработчики от тестировщиков в виде отчетов о проблемах. Подробные отчеты о проблемах, составленные на этапе модульного тестирования, позволяют локализовать и устранить многие дефекты в программной системе на ранних стадиях ее разработки или разработки ее новой функциональности. Информационная закрытость. Связность. Виды связности Принцип информационной закрытости утверждает: содержание модулей должно быть скрыто друг от друга. Модуль должен определяться и проектироваться так, чтобы его содержимое (процедуры и данные) было недоступно тем модулям, которые не нуждаются в такой информации (клиентам). Информационная закрытость означает следующее: 1) все модули независимы, обмениваются только информацией, необходимой для работы; 2) доступ к операциям и структурам данных модуля ограничен. Достоинства информационной закрытости: обеспечивается возможность разработки независимыми коллективами; 23 модулей различными, обеспечивается легкая модификация системы (вероятность распространения ошибок очень мала, так как большинство данных и процедур скрыто от других частей системы). Идеальный модуль играет роль «черного ящика», содержимое которого невидимо клиентам. Он прост в использовании — количество «ручек и органов управления» им невелико (аналогия с эксплуатацией телевизора). Его легко развивать и корректировать в процессе сопровождения программной системы. Для обеспечения таких возможностей система внутренних и внешних связей модуля должна отвечать особым требованиям. Связность модуля (Cohesion) — это мера зависимости его частей. Связность — внутренняя характеристика модуля. Чем выше связность модуля, тем лучше результат проектирования, то есть тем «черней» его ящик (капсула, защитная оболочка модуля), тем меньше «ручек управления» на нем находится и тем проще эти «ручки». Для измерения связности используют понятие силы связности (СС). Существует 7 типов связности: 1. Связность по совпадению (СС=0). В модуле отсутствуют явно выраженные внутренние связи. 2. Логическая связность (СС=1). Части модуля объединены по принципу функционального подобия. Например, модуль состоит из разных подпрограмм обработки ошибок. При использовании такого модуля клиент выбирает только одну из подпрограмм. Недостатки: сложное сопряжение; большая вероятность внесения ошибок при изменении сопряжения ради одной из функций. 3. Временная связность (СС=3). Части модуля не связаны, но необходимы в один и тот же период работы системы. Недостаток: сильная взаимная связь с другими модулями, отсюда — сильная чувствительность внесению изменений. 4. Процедурная связность (СС=5). Части модуля связаны порядком выполняемых ими действий, реализующих некоторый сценарий поведения. 24 5. Коммуникативная связность (СС=7). Части модуля связаны по данным (работают с одной и той же структурой данных). 6. Информационная (последовательная) связность (СС=9). Выходные данные одной части используются как входные данные в другой части модуля. 7. Функциональная связность (СС=10). Части модуля вместе реализуют одну функцию. Сцепление. Типы сцепления. Сцепление модулей - это мера относительной независимости модулей. Слабое сцепление определяет высокий уровень независимости модулей. Модули являются полностью независимыми, если каждый из них не содержит о другом никакой информации. Чем больше информации о другом модуле в них используется, тем менее они независимы и тем более сцеплены. Независимое сцепление возможно, если модули не вызывают друг друга и не обрабатывают одну и ту же информацию. Модули сцеплены по данным, если они имеют общие простые элементы данных, которые передаются от одного модуля к другому как параметры. (пр, структура – массив, а передается в качестве параметра элемент массива; при этом изменения в структуре данных не повлияют на другой модуль). Модули со сцеплением по данным не имеют общей области данных (глобальных переменных). Модули сцеплены по образцу, если в качестве параметров используются структуры данных (например, в качестве параметра передается массив). Недостаток: оба модуля должны содержать информацию о другом модуле (внутренней структуре данных), т.е изменения должны отображаться и в др. модуле. Модули сцеплены по общей области, если они разделяют одну и ту же глобальную структуру данных. 25 Модули сцеплены по управлению, если какой-либо из них управляет решениями внутри другого с помощью передачи флагов, переключателей и т.д. Модуль сцеплен по внешним ссылкам, если у него есть доступ к данным другого модуля через внешнюю точку входа. Модули сцеплены по кодам, если коды их команд объединены друг с другом, используют общий участок памяти. Если модули косвенно обращаются друг к другу (ч/з другие модули), то между ними также существует сцепление. Сцепление модулей зависит от спроектированной структуры данных и способов взаимодействия между модулями. Необходимо использовать простые параметры и не применять глобальных данных Тема 6 Основы работы с базами данных Специальные библиотеки. Многие языки программирования имеют встроенные средства взаимодействия с СУБД, однако, другие требуют сторонних пакетов. Рассмотрим SQL‑библиотеки Python, которые для этого можно использовать: SQLite, MySQL и PostgreSQL. SQLite, вероятно, самая простая база данных, к которой можно подключиться из приложения Python и для этого нет необходимости подключать внешние модули. По умолчанию установка Python содержит SQL‑библиотеку с именем sqlite3, которую мы можем использовать для работы с SQLite. Более того, базы данных SQLite являются безсерверными и автономными, поскольку они читают и записывают данные в файл. Это означает, что, в отличие от MySQL и PostgreSQL, не нужно устанавливать и запускать сервер для выполнения операций с базой данных SQLite. 26 В отличие от SQLite для подключения к базе данных MySQL требуется дополнительный модуль и нужно установить драйвер Python SQL. Одним из таких драйверов является mysql-connector-python. Можно загрузить этот модуль Python SQL с помощью pip: $ pip install mysql-connector-python Обратите внимание, что MySQL является серверной системой управления базами данных. Один сервер MySQL может иметь несколько баз данных. В отличие от SQLite, где создание соединения равносильно созданию базы данных, база данных MySQL имеет двухступенчатый процесс создания базы данных: Установите соединение с сервером MySQL. Выполните отдельный запрос, чтобы создать базу данных. В библиотеке SQL Python, как и для MySQL нет функций, которые можно использовать для работы с базой данных PostgreSQL. Вместо этого нужно установить сторонний драйвер SQL для взаимодействия с PostgreSQL. Одним из таких драйверов SQL для PostgreSQL является psycopg2. Базовый синтаксис SQL. Подмножества языка SQL: ● Язык DDL (Data Definition Language) служит для создания и модификации структуры БД, т.е. для создания/изменения/удаления таблиц и связей. ● Язык DML (Data Manipulation Language) позволяет осуществлять манипуляции с данными таблиц, т.е. с ее строками. Он позволяет делать выборку данных из таблиц, добавлять новые данные в таблицы, а также обновлять и удалять существующие данные. ● Язык DCL (Transaction Control Language) предназначен для осуществления административных операций, присваивающих или отменяющих право (привилегию) использовать базу данных, таблицы и другие объекты базы данных, а также выполнять те или иные операторы SQL. 27 ● Язык TCL (Transaction Control Language) - используется для обработки транзакций. В соответствии подмножеством языка SQL, операторы SQL делятся на: ● операторы определения данных (DDL): ○ CREATE создаёт объект базы данных (саму базу, таблицу, представление, пользователя и так далее), ○ ALTER изменяет объект, ○ DROP удаляет объект; ● операторы манипуляции данными (DML): ○ SELECT выбирает данные, удовлетворяющие заданным условиям, ○ INSERT добавляет новые данные, ○ UPDATE изменяет существующие данные, ○ DELETE удаляет данные; ● операторы определения доступа к данным (DCL): ○ GRANT предоставляет пользователю (группе) разрешения на определённые операции с объектом, ○ REVOKE отзывает ранее выданные разрешения, ○ DENY задаёт запрет, имеющий приоритет над разрешением; ● операторы управления транзакциями (TCL): ○ COMMIT применяет транзакцию, ○ ROLLBACK откатывает все изменения, сделанные в контексте текущей транзакции, ○ SAVEPOINT делит транзакцию на более мелкие участки. SELECT - Возвращает строки из базы данных и позволяет делать выборку одной или нескольких строк или столбцов из одной или нескольких таблиц. Базовый синтаксис команды: SELECT [DISTINCT] список_столбцов или * FROM источник 28 WHERE фильтр ORDER BY выражение_сортировки Создание таблицы, работа с данными. Для установки соединения нужно указать название базы данных, к которой требуется подключиться. Если указать название той, что уже есть на диске, то произойдет подключение. Если же указать другое, то SQLite создаст новую базу данных. Для подключения к SQLite нужно выполнить следующие шаги: Использовать метод connect() из модуля sqlite3 и передать в качестве аргумента название базы данных. Создать объект cursor с помощью объекта соединения, который вернул прошлый метод для выполнения SQLite-запросов из Python. Закрыть объект cursor после завершения работы. Перехватить исключение базы данных, если в процессе подключения произошла ошибка. Понимание SQLite-подключения в подробностях import sqlite3 Эта строка импортирует в программу модуль sqlite3. С помощью классов и методов из этого модуля можно взаимодействовать с базой данных SQLite. sqlite3.connect() С помощью метода connect() выполняется подключение к базе данных. Этот метод возвращает объект подключения SQLite. Объект connection не является потокобезопасным. Модуль sqlite3 не позволяет делиться подключением между потоками. Если попытаться сделать это, то можно получить исключение. Метод connect() принимает разные аргументы. В этом примере передается название базы данных. cursor=sqliteConnection.cursor() 29 С помощью объекта соединения создается объект cursor, который позволяет выполнять SQLite-запросы из Python. Для одного соединения можно создать неограниченное количество cursor. Он также не является потокобезопасным. Модуль не позволяет делиться объектами cursor между потоками. Если это сделать, то будет ошибка. После этого создается запрос для получения версии базы данных. cursor.execute() С помощью метода execute объекта cursor можно выполнить запрос в базу данных из Python. Он принимает SQLite-запрос в качестве параметра и возвращает resultSet — то есть, строки базы данных Получить результат запроса из resultSet можно с помощью методов, например, fetchAll() cursor.close() и connection.close() Хорошей практикой считается закрывать объекты connection и curosor после завершения работы, чтобы избежать проблем с базой данных. Шаги для создания таблицы в SQLite с помощью Python: Соединиться с базой данных с помощью sqlite3.connect(). Подготовить запрос создания таблицы. Выполнить запрос с помощью cursor.execute(query). Закрыть соединение с базой и объектом cursor. Типы данных SQLite: NULL — значение NULL INTEGER — числовые значения. Целые числа хранятся в 1, 2, 3, 4, 6 и 8 байтах в зависимости от величины REAL — числа с плавающей точкой, например, 3.14, число Пи TEXT — текстовые значения. Могут храниться в кодировке UTF-8, UTF-16BE или UTF-16LE BLOB — бинарные данные. Для хранения изображений и файлов 30 Python DB-API модули Несмотря на стандарт SQL (ISO/IEC 9075), отдельные СУБД имеют много различий. Чтобы программистам не вникать в реализацию каждой из них, придумали общее API, скрывающее эти детали. Вынесенная в заголовок аббревиатура объединяет два понятия: DB (Database, база данных) и API (Application Program Interface, интерфейс прикладной программы). Таким образом, DB-API определяет интерфейс прикладной программы с базой данных. Этот интерфейс должны реализовывать все модули расширения, которые служат для связи Python-программ с базами данных. Единый API (в настоящий момент его вторая версия) позволяет абстрагироваться от марки используемой базы данных, при необходимости довольно легко менять одну СУБД на другую, изучив всего один набор функций и методов. DB API 2.0 регламентирует интерфейсы модуля расширения для работы с базой данных, методы объекта-соединения с базой, объекта-курсора текущей обрабатываемой записи, объектов различных для типов данных и их конструкторов, а также содержит рекомендации для разработчиков по реализации модулей. На сегодня Python поддерживает через модули расширения многие известные базы данных. Доступ к базе данных осуществляется с помощью объекта-соединения (connection object). DB-API-совместимый модуль должен предоставлять функцию-конструктор connect() для класса объектов-соединений. Конструктор должен иметь следующие именованные параметры: dsn Название источника данных в виде строки user Имя пользователя password Пароль host Адрес хоста, на котором работает СУБД database Имя базы данных. Модуль определяет константы, содержащие его основные характеристики: 31 apilevel Версия DB-API ("1.0" или "2.0"). threadsafety Целочисленная константа, описывающая возможности модуля при использовании потоков управления: 0 Модуль не поддерживает потоки. 1 Потоки могут совместно использовать модуль, но не соединения. 2 Потоки могут совместно использовать модуль и соединения. 3 Потоки могут совместно использовать модуль, соединения и курсоры. (Под совместным использованием здесь понимается возможность использования упомянутых ресурсов без применения семафоров). paramstyle Тип используемых пометок при подстановке параметров. Возможны следующие значения этой константы: "format" Форматирование в стиле языка ANSI C (например, "%s", "%i" ). "pyformat" Использование именованных спецификаторов формата в стиле Python ( "%(item)s" ) "qmark" Использование знаков "?" для пометки мест подстановки параметров. "numeric" Использование номеров позиций ( ":1" ). "named" Использование имен подставляемых параметров ( ":name" ). Модуль должен определять ряд исключений для обозначения типичных исключительных ситуаций: Warning (предупреждение), Error (ошибка), InterfaceError (ошибка интерфейса), DatabaseError (ошибка, относящаяся к базе данных). А также подклассы этого последнего исключения: DataError (ошибка обработки данных), OperationalError (ошибка в работе или сбой соединения с базой данных), IntegrityError (ошибка целостности базы данных), InternalError (внутренняя ошибка базы данных), ProgrammingError (программная ошибка, например, ошибка в синтаксисе SQL-запроса), NotSupportedError (при отсутствии поддержки запрошенного свойства). 32 Объектно-реляционное отображение (ORM) ORM или Object-relational mapping (рус. Объектно-реляционное отображение) — это технология программирования, которая позволяет преобразовывать несовместимые типы моделей в ООП, в частности, между хранилищем данных и объектами программирования. ORM используется для упрощения процесса сохранения объектов в реляционную базу данных и их извлечения, при этом ORM сама заботится о преобразовании данных между двумя несовместимыми состояниями. Большинство ORM-инструментов в значительной мере полагаются на метаданные базы данных и объектов, так что объектам ничего не нужно знать о структуре базы данных, а базе данных — ничего о том, как данные организованы в приложении. ORM обеспечивает полное разделение задач в хорошо спроектированных приложениях, при котором и база данных, и приложение могут работать с данными каждый в своей исходной форме. Использование ORM решает проблему так называемой парадигмы «несоответствия», которая гласит о том, что объектные и реляционные модели не очень хорошо работают вместе. Реляционные базы представляют данные в табличном формате, в то время как объектно-ориентированные языки представляют их как связанный граф объектов. Основные проблемы и несоответствия возникают во время сохранения этого графа объектов в реляционную базу или его загрузки: реляционная модель может быть намного детальнее, чем объектная, т.е. для хранения одного объекта в реляционной базе данных используется несколько таблиц; реляционные СУБД не имеют ничего похожего на наследование — естественную парадигму объектно-ориентированных языков программирования; в СУБД определен только один параметр для сравнения записей — первичный ключ. В то время как ООП предоставляет как проверку идентичности объектов (a==b), так и их равенства (a.equals(b)); 33 для связи объектов СУБД использует понятие внешних ключей, в объектно-ориентированных языках связь между объектами может быть только однонаправленной. Если же нужно организовать двунаправленные отношения, то придется определить две однонаправленные ассоциации. Ключевой особенностью ORM является отображение, которое используется для привязки объекта к его данным в БД. ORM как бы создает «виртуальную» схему базы данных в памяти и позволяет манипулировать данными уже на уровне объектов. Отображение показывает, как объект и его свойства связанны с одной или несколькими таблицами и их полями в базе данных. ORM использует информацию этого отображения для управления процессом преобразования данных между базой и формами объектов, а также для создания SQL-запросов для вставки, обновления и удаления данных в ответ на изменения, которые приложение вносит в эти объекты. 34 Тема 7 Разработка пользовательского интерфейса Правила разработки интерфейсов пользователя Правило 1: дать контроль пользователю Принципы, которые дают пользователю контроль над системой: 1)использовать режимы благоразумно; 2)предоставить пользователю возможность выбирать: работать либо мышью, либо клавиатурой, либо их комбинацией; 3)позволить пользователю сфокусировать внимание; 4)демонстрировать сообщения, которые помогут ему в работе; 5)создать условия для немедленных и обратимых действий, а также обратной связи; 6)обеспечить соответствующие пути и выходы; 7)приспосабливайте систему к пользователям с различным уровнем подготовки; 8)сделать пользовательский интерфейс более понятным; 9)дать пользователю возможность настраивать интерфейс по своему вкусу; 10)разрешить пользователю напрямую манипулировать объектами интерфейса; Правило 2: уменьшить нагрузку на пользователя Основанная на знании того, как люди хранят и запоминают информацию, сила компьютерного интерфейса должна защитить память людей от излишней загруженности. Принципы, позволяющие снизить нагрузку на память пользователя: 1)не загружать кратковременную память; 2)полагаться на распознавание, а не на повторение; 3)представить визуальные заставки; 4)предусмотреть установки по умолчанию, команды Undo и Rendo; 5)предусмотреть "быстрые" пути; 35 6)активировать синтаксис действий с объектами; 7)использовать метафоры из реального мира; 8)применять раскрытие и объяснение понятий и действий; 9)увеличить визуальную ясность. Правило 3: сделать интерфейс совместимым Совместимость — ключевой аспект для использования интерфейса. Однако не следует во что бы то ни стало стремиться к ней. Одним из основных преимуществ последовательности является то, что пользователи могут перенести свои знания и навыки из старой программы, которой они пользовались раньше, в новую. Принципы создания совместимости интерфейса: 1)проектирование последовательного интерфейса; 2)общая совместимость всех программ; 3)сохранение результатов взаимодействия; 4)эстетическая привлекательность и цельность; 5)поощрение изучения; Требования к интерфейсу Основные требования к пользовательскому интерфейсу: функциональность (соответствие задачам пользователя); соответствие технологии; понятность и логичность; обеспечение высокой скорости работы пользователя; обеспечение защиты от человеческих ошибок; быстрое обучение пользователя; субъективное удовлетворение пользователя. На основе общих требований к пользовательскому сформирована система требований к его элементам управления. Требования к названию (тексту) элементов управления: 36 интерфейсу название элемента должно отражать его функцию; названия элементов должны быть краткими, но понятными пользователю; наиболее значимое слово должно стоять в названии элемента первым; для названия элемента, запускающего действие, целесообразно использовать глагол в форме инфинитива; если элемент меню служит для запуска окна с продолжением диалога, то в конце его названия следует ставить многоточие; пиктограммами следует снабжать только самые важные элементы меню Требования к расположению элементов управления: элементы меню разделительными следует группировать; полосками либо группы «визуальными следует разделять паузами»; часто используемые элементы целесообразно располагать в левой верхней части экрана, редко используемые - в правой нижней части; терминационные кнопки (т.е. командные кнопки, управляющие окном, например, «Ок», «Отмена», «Применить», «Закрыть») должны быть расположены либо внизу окна, либо в правой его части (т.е. в той части окна, которая сканируется взглядом в последнюю очередь); хорошо, если диалоговое окно читается, как текст: один элемент управления однозначно преобразовывается во фрагмент предложения, а единая группа элементов - в целое предложение. Требования к оформлению чекбоксов и радиокнопок: не следует нарушать сложившуюся традицию: чекбоксы выглядят как квадраты, а радиокнопки - как кружки; и чекбоксы, и радиокнопки желательно расставлять по вертикали, поскольку это значительно ускоряет поиск нужного элемента; радиокнопки всегда должны находиться в рамке группировки, а для чекбоксов это необязательно; в группе радиокнопок как минимум одна радиокнопка должна быть проставлена по умолчанию; каждая подпись чекбокса и радиокнопки должна однозначно показывать эффектот выбора соответствующего элемента; поскольку радиокнопки и чекбоксы не вызывают немедленного действия, формулировать подписи к ним лучше всего в форме существительных, хотя возможно использование глаголов; подписи к стоящим параллельно кнопкам лучше стараться делать примерно одинаковой 37 длины; все подписи лучше делать позитивными (т.е. не содержащими отрицания); не следует повторять в подписях одни и те же слова, меняя только окончания подписей (например, «Показывать пробелы» и «Показывать табуляции») - лучше перенести повторяющееся слово в рамку группировки; если подпись не помещается в одну строку, то индикатор кнопки (кружок или квадрат) следует выравнивать по первой строке подписи; подписи к чекбоксам и радиокнопкам должны быть нажимабельными (это связано с тем, что для быстроты и точности работы пользователю нужны большие кнопки); при необходимости заблокировать элемент чекбокса или радиокнопки, желательно визуально ослаблять не только индикатор кнопки (квадрат или круг), но и подпись к нему. Анализ интерфейса Анализ сложности интерфейса — это метод оценки юзабилити конкретной задачи или набора задач, который не требует непосредственного участия пользователя и даёт количественные результаты. Какие параметры нужно оценить 1. Изменения контекста: движение пользователя внутри продукта для завершения шага. Пример: незначительное изменение контекста при переносе пользователя на новую страницу 2. Помощь в навигации: предоставляется поддержка для начала и завершения шага. Пример: текст о том, как начать работу, поможет узнать основные возможности продукта 3. Входные параметры: информация, которую пользователь должен уточнить для завершения шага. Пример: пользователю нужно выбрать параметры с помощью кнопок 4. Обратная связь системы: реакция системы на действия пользователя во время выполнения шага. Пример: журнал уведомлений в режиме реального времени отражает шаги, предпринятые пользователем 38 5. Обратная связь об ошибках: реакция системы на типичные ошибки пользователя. Пример: это сообщение об ошибке рассказывает пользователю, почему шаг не удался, но не даёт рекомендаций по устранению проблемы 6. Новые понятия: информация, которую пользователь должен понять для конкретного шага. Пример: модальное окно предоставляет обзор функций, но если нажать на ссылку для получения дополнительной информации, это выведет пользователя из продукта Универсальных рекомендаций в анализе интерфейсов вообще не бывает. Именно поэтому UX-аудит не сводится к составлению документа. Самое главное в задачах анализа сложившегося интерфейса — это предметное обсуждение результатов с теми, кто в силах что-то изменить. С владельцами продукта, разработчиками и другими специалистами. И вся квалификация эксперта сосредоточена именно в этом разговоре, а не в подготовительных материалах к нему. Основные возможные проблемы: Непонятные элементы и сложная процедура решения базовых задач пользователя Плохо работает поиск и фильтрация товаров Непонятное описание характеристик и некорректное изображение товара Фотографии товара не дают представления о внешнем виде товара. Непонятные условия акций, спецпредложений, товаров в подарок Непонятные формулировки и условия акций. Неправильный подбор сопутствующих товаров и неудобное для характеристиками, и пользователя размещение на сайте Сопутствующие товары размещены перед пользователь их проматывает, не посмотрев. 39 Тема 8 Архитектурные шаблоны в проектировании Назначение и виды паттернов Паттерн проектирования — это часто встречающееся решение определённой проблемы при проектировании архитектуры программ. В отличие от готовых функций или библиотек, паттерн нельзя просто взять и скопировать в программу. Паттерн представляет собой не какой-то конкретный код, а общую концепцию решения той или иной проблемы, которую нужно будет ещё подстроить под нужды вашей программы. Паттерны часто путают с алгоритмами, ведь оба понятия описывают типовые решения каких-то известных проблем. Но если алгоритм — это чёткий набор действий, то паттерн — это высокоуровневое описание решения, реализация которого может отличаться в двух разных программах. Если привести аналогии, то алгоритм — это кулинарный рецепт с чёткими шагами, а паттерн — инженерный чертёж, на котором нарисовано решение, но не конкретные шаги его реализации. Описания паттернов обычно очень формальны и чаще всего состоят из таких пунктов: проблема, которую решает паттерн; мотивации к решению проблемы способом, который предлагает паттерн; структуры классов, составляющих решение; примера на одном из языков программирования; особенностей реализации в различных контекстах; связей с другими паттернами. Такой формализм в описании позволил создать обширный каталог паттернов, проверив каждый из них на состоятельность. Самые низкоуровневые и простые паттерны — идиомы. Они не универсальны, поскольку применимы только в рамках одного языка программирования. 40 Самые универсальные — архитектурные паттерны, которые можно реализовать практически на любом языке. Они нужны для проектирования всей программы, а не отдельных её элементов. Кроме того, паттерны отличаются и предназначением: Порождающие паттерны беспокоятся о гибком создании объектов без внесения в программу лишних зависимостей. Структурные паттерны показывают различные способы построения связей между объектами. Поведенческие паттерны заботятся об эффективной коммуникации между объектами. Порождающие паттерны Abstract Factory, Builder, Factory method Фабричный метод — это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов. Паттерн Фабричный метод предлагает создавать объекты не напрямую, используя оператор new, а через вызов особого фабричного метода. Объекты всё равно будут создаваться при помощи new, но делать это будет фабричный метод. Чтобы эта система заработала, все возвращаемые объекты должны иметь общий интерфейс. Подклассы смогут производить объекты различных классов, следующих одному и тому же интерфейсу. Пока все продукты реализуют общий интерфейс, их объекты можно взаимозаменять в клиентском коде. Для клиента фабричного метода нет разницы между этими объектами, так как он будет трактовать их как некий абстрактный Транспорт. Для него будет важно, чтобы объект имел метод доставить, а как конкретно он работает — не важно. 41 Абстрактная фабрика — это порождающий паттерн проектирования, который позволяет создавать семейства связанных объектов, не привязываясь к конкретным классам создаваемых объектов. Для начала паттерн Абстрактная фабрика предлагает выделить общие интерфейсы для отдельных продуктов, составляющих семейства. Так, все вариации кресел получат общий интерфейс Кресло, все диваны реализуют интерфейс Диван и так далее. Все вариации одного и того же объекта должны жить в одной иерархии классов. Далее вы создаёте абстрактную фабрику — общий интерфейс, который содержит методы создания всех продуктов семейства (например, создатьКресло, создатьДиван и создатьСтолик). Клиентский код должен работать как с фабриками, так и с продуктами только через их общие интерфейсы. Это позволит подавать в ваши классы любой тип фабрики и производить любые продукты, ничего не ломая. Для клиентского кода должно быть безразлично, с какой фабрикой работать. Строитель — это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов. Паттерн Строитель предлагает вынести конструирование объекта за пределы его собственного класса, поручив это дело отдельным объектам, называемым строителями. Строитель позволяет создавать сложные объекты пошагово. Промежуточный результат всегда остаётся защищён. Паттерн предлагает разбить процесс конструирования объекта на отдельные шаги (например, построитьСтены, вставитьДвери и другие). Чтобы создать объект, вам нужно поочерёдно вызывать методы строителя. Причём не нужно запускать все шаги, а только те, что нужны для производства объекта определённой конфигурации. 42 Порождающие паттерны: Prototype, Singleton Прототип — это порождающий паттерн проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации. Паттерн Прототип поручает создание копий самим копируемым объектам. Он вводит общий интерфейс для всех объектов, поддерживающих клонирование. Это позволяет копировать объекты, не привязываясь к их конкретным классам. Обычно такой интерфейс имеет всего один метод clone. Реализация этого метода в разных классах очень схожа. Метод создаёт новый объект текущего класса и копирует в него значения всех полей собственного объекта. Так получится скопировать даже приватные поля, так как большинство языков программирования разрешает доступ к приватным полям любого объекта текущего класса. Объект, который копируют, называется прототипом (откуда и название паттерна). Когда объекты программы содержат сотни полей и тысячи возможных конфигураций, прототипы могут служить своеобразной альтернативой созданию подклассов. Одиночка — это порождающий паттерн проектирования, который гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа. Одиночка решает сразу две проблемы, нарушая принцип единственной ответственности класса. o Гарантирует наличие единственного экземпляра класса. o Глобальный доступ к одному объекту Все реализации одиночки сводятся к тому, чтобы скрыть конструктор по умолчанию и создать публичный статический метод, который и будет контролировать жизненный цикл объекта-одиночки. 43 Если у вас есть доступ к классу одиночки, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его ни вызвали, он всегда будет отдавать один и тот же объект. Структурные паттерны: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. Вы можете создать адаптер. Это объект-переводчик, который трансформирует интерфейс или данные одного объекта в такой вид, чтобы он стал понятен другому объекту. При этом адаптер оборачивает один из объектов, так что другой объект даже не знает о наличии первого. Например, вы можете обернуть объект, работающий в метрах, адаптером, который бы конвертировал данные в футы. Адаптеры могут не только переводить данные из одного формата в другой, но и помогать объектам с разными интерфейсами работать сообща. Мост — это структурный паттерн проектирования, который разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга. Паттерн Мост предлагает заменить наследование агрегацией или композицией. Для этого нужно выделить одну из таких «плоскостей» в отдельную иерархию и ссылаться на объект этой иерархии, вместо хранения его состояния и поведения внутри одного класса. Компоновщик — это структурный паттерн проектирования, который позволяет сгруппировать множество объектов в древовидную структуру, а затем работать с ней так, как будто это единичный объект. Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки». 44 Наследование — это первое, что приходит в голову многим программистам, когда нужно расширить какое-то существующее поведение. Но механизм наследования имеет несколько досадных проблем. Одним из способов обойти эти проблемы является замена наследования агрегацией либо композицией. Это когда один объект содержит ссылку на другой и делегирует ему работу, вместо того чтобы самому наследовать его поведение. Как раз на этом принципе построен паттерн Декоратор. Фасад — это структурный паттерн проектирования, который предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку. Фасад — это простой интерфейс для работы со сложной подсистемой, содержащей множество классов. Фасад может иметь урезанный интерфейс, не имеющий 100% функциональности, которой можно достичь, используя сложную подсистему напрямую. Фасад полезен, если вы используете какуюто сложную библиотеку со множеством подвижных частей, но вам нужна только часть её возможностей. Легковес — это структурный паттерн проектирования, который позволяет вместить бóльшее количество объектов в отведённую оперативную память. Легковес экономит память, разделяя общее состояние объектов между собой, вместо хранения одинаковых данных в каждом объекте. Паттерн Легковес предлагает не хранить в классе внешнее состояние, а передавать его в те или иные методы через параметры. Таким образом, одни и те же объекты можно будет повторно использовать в различных контекстах. Но главное — понадобится гораздо меньше объектов, ведь теперь они будут отличаться только внутренним состоянием, а оно имеет не так много вариаций. Заместитель — это структурный паттерн проектирования, который позволяет подставлять вместо реальных объектов специальные объектызаменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то до или после передачи вызова оригиналу. 45 Поведенческие паттерны: Iterator, Observer, State, Strategy, Visitor, Template method Итератор — это поведенческий паттерн проектирования, который даёт возможность последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления. Наблюдатель — это поведенческий паттерн проектирования, который создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах. Состояние — это поведенческий паттерн проектирования, который позволяет объектам менять поведение в зависимости от своего состояния. Извне создаётся впечатление, что изменился класс объекта. Стратегия — это поведенческий паттерн проектирования, который определяет семейство схожих алгоритмов и помещает каждый из них в собственный класс, после чего алгоритмы можно взаимозаменять прямо во время исполнения программы. Паттерн Стратегия предлагает определить семейство схожих алгоритмов, которые часто изменяются или расширяются, и вынести их в собственные классы, называемые стратегиями. Вместо того, чтобы изначальный класс сам выполнял тот или иной алгоритм, он будет играть роль контекста, ссылаясь на одну из стратегий и делегируя ей выполнение работы. Чтобы сменить алгоритм, вам будет достаточно подставить в контекст другой объект-стратегию. Важно, чтобы все стратегии имели общий интерфейс. Используя этот интерфейс, контекст будет независимым от конкретных классов стратегий. С другой стороны, вы сможете изменять и добавлять новые виды алгоритмов, не трогая код контекста. Посетитель — это поведенческий паттерн проектирования, который позволяет добавлять в программу новые операции, не изменяя классы объектов, над которыми эти операции могут выполняться. Паттерн Посетитель предлагает разместить новое поведение в отдельном классе, вместо того чтобы 46 множить его сразу в нескольких классах. Объекты, с которыми должно было быть связано поведение, не будут выполнять его самостоятельно. Вместо этого вы будете передавать эти объекты в методы посетителя. Шаблонный метод — это поведенческий паттерн проектирования, который определяет скелет алгоритма, перекладывая ответственность за некоторые его шаги на подклассы. Паттерн позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры.Паттерн Шаблонный метод предлагает разбить алгоритм на последовательность шагов, описать эти шаги в отдельных методах и вызывать их в одном шаблонном методе друг за другом. Это позволит подклассам переопределять некоторые шаги алгоритма, оставляя без изменений его структуру и остальные шаги, которые для этого подкласса не так важны. Поведенческие паттерны: Chain of Responsibility, Memento, Command, Mediator Цепочка обязанностей — это поведенческий паттерн проектирования, который позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и стоит ли передавать запрос дальше по цепи. Снимок — это поведенческий паттерн проектирования, который позволяет сохранять и восстанавливать прошлые состояния объектов, не раскрывая подробностей их реализации. Паттерн Снимок поручает создание копии состояния объекта самому объекту, который этим состоянием владеет. Вместо того, чтобы делать снимок «извне», наш редактор сам сделает копию своих полей, ведь ему доступны все поля, даже приватные. Паттерн предлагает держать копию состояния в специальном объекте-снимке с ограниченным интерфейсом, позволяющим, например, узнать дату изготовления или название снимка. Но, с другой стороны, снимок должен быть открыт для своего создателя, позволяя прочесть и восстановить его внутреннее состояние. 47 Посредник — это поведенческий паттерн проектирования, который позволяет уменьшить связанность множества классов между собой, благодаря перемещению этих связей в один класс-посредник. Паттерн Посредник заставляет объекты общаться не напрямую друг с другом, а через отдельный объект-посредник, который знает, кому нужно перенаправить тот или иной запрос. Благодаря этому, компоненты системы будут зависеть только от посредника, а не от десятков других компонентов. Команда — это поведенческий паттерн проектирования, который превращает запросы в объекты, позволяя передавать их как аргументы при вызове методов, ставить запросы в очередь, логировать их, а также поддерживать отмену операций. Хорошие программы обычно структурированы в виде слоёв. Самый распространённый пример — слои пользовательского интерфейса и бизнес-логики. Первый всего лишь рисует красивую картинку для пользователя. Но когда нужно сделать что-то важное, интерфейс «просит» слой бизнес-логики заняться этим. В реальности это выглядит так: один из объектов интерфейса напрямую вызывает метод одного из объектов бизнес-логики, передавая в него какие-то параметры. Паттерн Команда предлагает больше не отправлять такие вызовы напрямую. Вместо этого каждый вызов, отличающийся от других, следует завернуть в собственный класс с единственным методом, который и будет осуществлять вызов. Такие объекты называют командами. К объекту интерфейса можно будет привязать объект команды, который знает, кому и в каком виде следует отправлять запросы. Когда объект интерфейса будет готов передать запрос, он вызовет метод команды, а та — позаботится обо всём остальном. 48 Тема 9 Конструирование ПО Рефакторинг. Определение, причины и цели. Приемы рефакторинга Рефакторинг — это контролируемый процесс улучшения кода, без написания новой функциональности. Результат рефакторинга — это чистый код и простой дизайн. Грязный код является не только результатом лени и невежества, но и побочным продуктом частых изменений в процессе разработки. Чистый код — это код, который просто читать, понимать и поддерживать. Чистый код улучшает предсказуемость разработки и повышает качество продукта. Приёмы рефакторинга Составление методов Рефакторинги этой группы призваны уменьшить сложность внутри метода, убрать дублирование кода и облегчить последующую работу с ним. Перемещение функций между объектами Рефакторинги этой группы показывают как безопасно перемещать функциональность из одних классов в другие, создавать новые классы, а также скрывать детали реализации из публичного доступа. Организация данных Рефакторинги этой группы призваны облегчить работу с данными, заменив работу с примитивными типами богатыми функциональностью классами. Кроме того, важным моментом является уменьшение связанности между классами, что улучшает переносимость классов и шансы их повторного использования. Упрощение условных выражений Логика условного выполнения имеет тенденцию становиться сложной, поэтому ряд рефакторингов направлен на то, чтобы упростить ее. Упрощение вызовов методов 49 Эти рефакторинги делают вызовы методов проще и яснее для понимания. Это, в свою очередь, упрощает интерфейсы взаимодействия между классами. Решение задач обобщения Обобщение порождает собственную группу рефакторингов, в основном связанных с перемещением функциональности по иерархии наследования классов, создания новых классов и интерфейсов, а также замены наследования делегированием и наоборот. Составление методов. Рефакторинги этой группы призваны уменьшить сложность внутри метода, убрать дублирование кода и облегчить последующую работу с ним. Извлечение метода Проблема: У вас есть фрагмент кода, который можно сгруппировать. Решение: Выделите участок кода в новый метод (или функцию) и вызовите этот метод вместо старого кода. Встраивание метода Проблема: Стоит использовать в том случае, когда тело метода очевиднее самого метода. Решение: Замените вызовы метода его содержимым и удалите сам метод. Извлечение переменной Проблема: У вас есть сложное для понимания выражение. Решение: Поместите результат выражения или его части в отдельные переменные, поясняющие суть выражения. Встраивание переменной Проблема: У вас есть временная переменная, которой присваивается результат простого выражения (и больше ничего). Решение: Замените обращения к переменной этим выражением. Замена переменной вызовом метода Проблема: Вы помещаете результат какого-то выражения в локальную переменную, чтобы использовать её далее в коде. 50 Решение: Выделите все выражение в отдельный метод и возвращайте результат из него. Замените использование вашей переменной вызовом метода. Новый метод может быть использован и в других методах. Расщепление переменной Проблема: У вас есть локальная переменная, которая используется для хранения разных промежуточных значений внутри метода (за исключением переменных циклов). Решение: Используйте разные переменные для разных значений. Каждая переменная должна отвечать только за одну определённую вещь. Удаление присваиваний параметрам Проблема: Параметру метода присваивается какое-то значение. Решение: Вместо параметра воспользуйтесь новой локальной переменной. Замена метода объектом методов Проблема: У вас есть длинный метод, в котором локальные переменные так сильно переплетены, что это делает невозможным применение «извлечения метода». Решение: Преобразуйте метод в отдельный класс так, чтобы локальные переменные стали полями этого класса. После этого можно без труда разделить метод на части. Замена алгоритма Проблема: Вы хотите заменить существующий алгоритм другим? Решение: Замените тело метода, реализующего старый алгоритм, новым алгоритмом. Перемещение функций между объектами Если вы разместили функциональность по классам не самым удачным образом — это еще не повод отчаиваться. 51 Рефакторинги этой группы показывают как безопасно перемещать функциональность из одних классов в другие, создавать новые классы, а также скрывать детали реализации из публичного доступа. Перемещение метода Проблема: Метод используется в другом классе больше, чем в собственном. Решение: Создайте новый метод в классе, который использует его больше других, и перенесите туда код из старого метода. Код оригинального метода превратите в обращение к новому методу в другом классе либо уберите его вообще. Перемещение поля Проблема: Поле используется в другом классе больше, чем в собственном. Решение: Создайте поле в новом классе и перенаправьте к нему всех пользователей старого поля. Извлечение класса Проблема: Один класс работает за двоих. Решение: Создайте новый класс, переместите в него поля и методы, отвечающие за определённую функциональность. Встраивание класса Проблема: Класс почти ничего не делает, ни за что не отвечает, и никакой ответственности для этого класса не планируется. Решение: Переместите все фичи из описанного класса в другой. Сокрытие делегирования Проблема: Клиент получает объект B из поля или метода объекта А. Затем клиент вызывает какой-то метод объекта B. Решение: Создайте новый метод в классе А, который бы делегировал вызов объекту B. Таким образом, клиент перестанет знать о классе В и зависеть от него. Удаление посредника Проблема: Класс имеет слишком много методов, которые просто делегируют работу другим объектам. 52 Решение: Удалите эти методы и заставьте клиента вызывать конечные методы напрямую. Введение внешнего метода Проблема: Служебный класс не содержит метода, который вам нужен, при этом у вас нет возможности добавить метод в этот класс. Решение: Добавьте метод в клиентский класс и передавайте в него объект служебного класса в качестве аргумента. Введение локального расширения Проблема: В служебном классе отсутствуют некоторые методы, которые вам нужны. При этом добавить их в этот класс вы не можете. Решение: Создайте новый класс, который бы содержал эти методы, и сделайте его наследником служебного класса, либо его обёрткой. Организация данных. Рефакторинги этой группы призваны облегчить работу с данными, заменив работу с примитивными типами богатыми функциональностью классами. Кроме того, важным моментом является уменьшение связанности между классами, что улучшает переносимость классов и шансы их повторного использования. Самоинкапсуляция поля Проблема: Вы используете прямой доступ к приватным полями внутри класса. Решение: Создайте геттер и сеттер для поля, и пользуйтесь для доступа к полю только ими. Замена простого поля объектом Проблема: В классе (или группе классов) есть поле простого типа. У этого поля есть своё поведение и связанные данные. Решение: Создайте новый класс, поместите в него старое поле и его поведения, храните объект этого класса в исходном классе. 53 Замена значения ссылкой Проблема: Есть много одинаковых экземпляров одного класса, которые можно заменить одним объектом. Решение: Превратите одинаковые объекты в один объект-ссылку. Замена ссылки значением Проблема: У вас есть объект-ссылка, который слишком маленький и неизменяемый, чтобы оправдать сложности по управлению его жизненным циклом. Решение: Превратите его в объект-значение. Замена поля-массива объектом Проблема: У вас есть массив, в котором хранятся разнотипные данные. Решение: Замените массив объектом, который будет иметь отдельные поля для каждого элемента. Дублирование видимых данных Проблема: Данные предметной области программы хранятся в классах, отвечающих за пользовательский интерфейс (GUI). Решение: Имеет смысл выделить данные предметной области в отдельные классы и, таким образом, обеспечить связь и синхронизацию между классом предметной области и GUI. Замена однонаправленной связи двунаправленной Проблема: У вас есть два класса, которым нужно использовать фичи друг друга, но между ними существует только односторонняя связь. Решение: Добавьте недостающую связь в класс, в котором она отсутствует. Замена двунаправленной связи однонаправленной Проблема: У вас есть двухсторонняя связь между классами, но один из классов больше не использует фичи другого. Решение: Уберите неиспользуемую связь. Замена магического числа символьной константой 54 Проблема: В коде используется число, которое несёт какой-то определённый смысл. Решение: Замените это число константой с человеко-читаемым названием, объясняющим смысл этого числа. Инкапсуляция поля Проблема: У вас есть публичное поле. Решение: Сделайте поле приватным и создайте для него методы доступа. Инкапсуляция коллекции Проблема: Класс содержит поле-коллекцию и простой геттер и сеттер для работы с этой коллекцией. Решение: Сделайте возвращаемое геттером значение доступным только для чтения и создайте методы добавления/удаления элементов этой коллекции. Замена кодирования типа классом Проблема: В классе есть поле, содержащее кодирование типа. Значения этого типа не используются в условных операторах и не влияют на поведение программы. Решение: Создайте новый класс и применяйте его объекты вместо значений закодированного типа. Замена кодирования типа подклассами Проблема: У вас есть закодированный тип, который непосредственно влияет на поведение программы (основываясь на значениях этого поля, в условных операторах выполняется различный код). Решение: Для каждого значения закодированного типа, создайте подклассы. А затем, вынесите соответствующие поведения из исходного класса в эти подклассы. Управляющий код замените полиморфизмом. Замена кодирования типа состоянием/стратегией Проблема: У вас есть закодированный тип, который влияет на поведение, но вы не можете использовать подклассы, чтобы избавиться от него. 55 Решение: Замените кодирование типа объектом-состоянием. При необходимости заменить значение поля с кодированием типа, в него подставляется другой объект-состояние. Замена подкласса полями Проблема: У вас есть подклассы, которые отличаются только методами, возвращающими данные-константы. Решение: Замените методы полями в родительском классе и удалите подклассы. Упрощение условных выражений Логика условного выполнения имеет тенденцию становиться сложной, поэтому ряд рефакторингов направлен на то, чтобы упростить ее. Разбиение условного оператора Проблема: У вас есть сложный условный оператор (if-then/else или switch). Решение: Выделите в отдельные методы все сложные части оператора: условие, then и else. Объединение условных операторов Проблема: У вас есть несколько условных операторов, ведущих к одинаковому результату или действию. Решение: Объедините все условия в одном условном операторе. Объединение дублирующихся фрагментов в условных операторах Проблема: Одинаковый фрагмент кода находится во всех ветках условного оператора. Решение: Вынесите его за рамки оператора. Удаление управляющего флага Проблема: У вас есть булевская переменная, которая играет роль управляющего флага для нескольких булевских выражений. Решение: Используйте break, continue и return вместо этой переменной. Замена вложенных условных операторов граничным оператором 56 Проблема: У вас есть группа вложенных условных операторов, среди которых сложно выделить нормальный ход выполнения кода. Решение: Выделите все проверки специальных или граничных случаев выполнения в отдельные условия и поместите их перед основными проверками. В идеале, вы должны получить «плоский» список условных операторов, идущих один за другим. Замена условного оператора полиморфизмом Проблема: У вас есть условный оператор, который, в зависимости от типа или свойств объекта, выполняет различные действия. Решение: Создайте подклассы, которым соответствуют ветки условного оператора. В них создайте общий метод и переместите в него код из соответствующей ветки условного оператора. Впоследствии замените условный оператор на вызов этого метода. Таким образом, нужная реализация будет выбираться через полиморфизм в зависимости от класса объекта. Введение Null-объекта Проблема: Из-за того, что некоторые методы возвращают null вместо реальных объектов, у вас в коде присутствует множество проверок на null. Решение: Вместо null возвращайте Null-объект, который предоставляет поведение по умолчанию. Введение проверки утверждения Проблема: Корректная работа участка кода предполагает наличие каких-то определённых условий или значений. Решение: Замените эти предположения конкретными проверками. Упрощение вызовов методов. Эти рефакторинги делают вызовы методов проще и яснее для понимания. Это, в свою очередь, упрощает интерфейсы взаимодействия между классами. Переименование метода Проблема: Название метода не раскрывает суть того, что он делает. 57 Решение: Измените название метода. Добавление параметра Проблема: Методу не хватает данных для осуществления каких-то действий. Решение: Создайте новый параметр, чтобы передать эти данные. Удаление параметра Проблема: Параметр не используется в теле метода. Решение: Удалите неиспользуемый параметр. Разделение запроса и модификатора Проблема: У вас есть метод, который возвращает какое-то значение, но при этом в процессе работы он изменяет что-то внутри объекта. Решение: Разделите метод на два разных метода. Один из них пускай возвращает значение, а второй модифицирует объект. Параметризация метода Проблема: Несколько методов выполняют похожие действия, которые отличаются только какими-то внутренними значениями, числами или операциями. Решение: Объедините все эти методы в один с параметром, в который будет передаваться отличающееся значение. Замена параметра набором специализированных методов Проблема: Метод разбит на части, каждая из которых выполняется в зависимости от значения какого-то параметра. Решение: Извлеките отдельные части метода в собственные методы и вызывайте их вместо оригинального метода. Передача всего объекта Проблема: Вы получаете несколько значений из объекта, а затем передаёте их в метод как параметры. Решение: Вместо этого передавайте весь объект. Замена параметра вызовом метода 58 Проблема: Вызываем метод и передаем его результаты как параметры другого метода. При этом значение параметров могли бы быть получены и внутри вызываемого метода. Решение: Вместо передачи значения через параметры метода, попробуйте переместить код получения значения внутрь самого метода. Замена параметров объектом Проблема: В ваших методах встречается повторяющаяся группа параметров. Решение: Замените эти параметры объектом. Удаление сеттера Проблема: Значение поля должно быть установлено только в момент создания и больше никогда не меняться. Решение: Удалите методы, устанавливающие значение этого поля. Сокрытие метода Проблема: Метод не используется другими классами либо используется только внутри своей иерархии классов. Решение: Сделайте метод приватным или защищённым. Замена конструктора фабричным методом Проблема: У вас есть сложный конструктор, делающий нечто большее, чем простая установка значений полей объекта. Решение: Создайте фабричный метод и замените им вызовы конструктора. Замена кода ошибки исключением Проблема: Метод возвращает определенное значение, которое будет сигнализировать об ошибке. Решение: Вместо этого следует выбрасывать исключение. Замена исключения проверкой условия Проблема: Вы выбрасываете исключение там, где можно было бы обойтись простой проверкой условия. Решение: Замените выбрасывание исключения проверкой этого условия. 59 Решение задач обобщения Обобщение порождает собственную группу рефакторингов, в основном связанных с перемещением функциональности по иерархии наследования классов, создания новых классов и интерфейсов, а также замены наследования делегированием и наоборот. Подъём поля Проблема: Два класса имеют одно и то же поле. Решение: Переместите поле в суперкласс, убрав его из подклассов. Подъём метода Проблема: Подклассы имеют методы, которые делают схожую работу. Решение: В этом случае нужно сделать методы идентичными, а затем переместить их в суперкласс. Подъём тела конструктора Проблема: Подклассы имеют конструкторы с преимущественно одинаковым кодом. Решение: Создайте конструктор в суперклассе и вынесите в него общий для подклассов код. Вызывайте конструктор суперкласса в конструкторах подкласса. Спуск метода Проблема: Поведение, реализованное в суперклассе, используется только одним или несколькими подклассами. Решение: Переместите это поведение в подклассы. Спуск поля Проблема: Поле используется только в некоторых подклассах. Решение: Переместите поле в эти подклассы. Извлечение подкласса Проблема: Класс имеет фичи, которые используются только в определённых случаях. Решение: Создайте подкласс и используйте его в этих случаях. 60 Извлечение суперкласса Проблема: У вас есть два класса с общими полями и методами. Решение: Создайте для них общий суперкласс и перенесите туда одинаковые поля и методы. Извлечение интерфейса Проблема: Несколько клиентов пользуются одной и той же частью интерфейса класса. Либо в двух классах часть интерфейса оказалась общей. Решение: Выделите эту общую часть в свой собственный интерфейс. Свёртывание иерархии Проблема: У вас есть некая иерархия классов, в которой подкласс мало чем отличается от суперкласса. Решение: Слейте подкласс и суперкласс воедино. Создание шаблонного метода Проблема: В подклассах реализованы алгоритмы, содержащие похожие шаги и одинаковый порядок выполнения этих шагов. Решение: Вынесите структуру алгоритма и одинаковые шаги в суперкласс, а в подклассах оставьте реализацию отличающихся шагов. Замена наследования делегированием Проблема: У вас есть подкласс, который использует только часть методов суперкласса или не хочет наследовать его данные. Решение: Создайте поле и поместите в него объект суперкласса, делегируйте выполнение методов объекту-суперклассу, уберите наследование. Замена делегирования наследованием Проблема: Класс содержит множество простых делегирующих методов ко всем методам другого класса. Решение: Сделайте класс наследником делегата, после чего делегирующие методы потеряют смысл. 61 Экстремальное программирование Экстремальное программирование или XP, eXtreme Programming — гибкая методология разработки программного обеспечения. Как и у других agileметодологий, у нее есть особенные инструменты, процессы и роли. Автор методики — американский разработчик Кент Бек. В конце 90-х годов он руководил проектом Chrysler Comprehensive Compensation System и там впервые применил практики экстремального программирования. Свой опыт и созданную концепцию он описал в книге Extreme Programming Explained, опубликованной в 1999 году. Цель методики XP — справиться с постоянно меняющимися требованиями к программному продукту и повысить качество разработки. Поэтому XP хорошо подходит для сложных и неопределенных проектов. Методология XP строится вокруг тестирования, дизайна и четырех процессов: кодирования, слушания. Кроме того, экстремальное программирование имеет ценности: простоту, коммуникацию, обратную связь, смелость и уважение. Тестирование. Виды, задачи Качество программного продукта характеризуется набором свойств, определяющих, насколько продукт «хорош» с точки зрения заинтересованных сторон, таких как заказчик продукта, спонсор, конечный пользователь, разработчики и тестировщики продукта, инженеры поддержки, сотрудники отделов маркетинга, обучения и продаж. Каждый из участников может иметь различное представление о продукте и том, насколько он хорош или плох, то есть о том, насколько высоко качество продукта. Таким образом, постановка задачи обеспечения качества продукта выливается в задачу определения заинтересованных лиц, их критериев качества и затем нахождения оптимального решения, удовлетворяющего этим критериям. Тестирование является одним из наиболее устоявшихся способов обеспечения качества 62 разработки программного обеспечения и входит в набор эффективных средств современной системы обеспечения качества программного продукта. С технической точки зрения тестирование заключается в выполнении приложения на некотором множестве исходных данных м сверке получаемых результатов с заранее известными (эталонными) с целью установить соответствие различных свойств и характеристик приложения заказанным свойствам. Отладка(debug, debugging) - процесс поиска, локализации и исправления ошибок в программе. Термин «отладка» в отечественной литературе используется двояко: для обозначения активности по поиску ошибок (собственно тестирование), по нахождению причин их появления и исправлению, или активности по локализации и исправлению ошибок. Тестирование - это процесс выполнения ПО системы или компонента в условиях анализа или записи получаемых результатов с целью проверки (оценки) некоторых свойств тестируемого объекта. Тестирование - это процесс анализа пункта требований к ПО с целью фиксации различий между существующим состоянием ПО и требуемым (что свидетельствует о проявлении ошибки) при экспериментальной проверке соответствующего пункта требований. Тестирование - это контролируемое выполнение программы на конечном множестве тестовых данных и анализ результатов этого выполнения для поиска ошибок. Целью проектирования тестовых вариантов является систематическое обнаружение различных классов ошибок при минимальных затратах времени и стоимости. Тестирование обеспечивает: • Обнаружение ошибок. • Демонстрацию соответствия функций программы ее назначению. • Демонстрацию реализации требований к характеристикам программы. • Отображение надежности как индикатора качества программы. 63 Тестирование не может показать отсутствие дефектов (оно может показывать только присутствие дефектов). Важно помнить это утверждение при проведении тестирования. Тестирование - важная часть любой программы контроля качества, а зачастую и единственная. Это печально, так как разнообразные методики совместной разработки позволяют находить больше ошибок, чем тестирование, и в то же время обходятся более чем вдвое дешевле в расчете на одну обнаруженную ошибку. Каждый из отдельных этапов тестирования (блочное тестирование, тестирование компонентов и интеграционное тестирование) обычно позволяют найти менее 50% ошибок. Комбинация этапов тестирования часто приводит к обнаружению менее 60% ошибок. Модульные тесты. Интеграционные тесты. Компонентное (модульное) тестирование (Component or Unit Testing) – это тестирование программы на уровне отдельно взятых модулей, функций или классов. Цель компонентного тестирования: выявление локализованных в модуле ошибок в реализации алгоритмов, а также определение степени готовности системы к переходу на следующий уровень разработки и тестирования. При компонентном тестировании проводятся unit-тесты. Unit-тесты тесты, проверяющие корректность работы отдельных модулей программы Интеграционное тестирование проверяет интерфейсы между компонентами, взаимодействие различных частей системы, таких как операционная системы, файловая система, аппаратное обеспечение, и интерфейсы между системами. Интеграционные тесты - тесты, проверяющие корректность взаимодействия отдельных модулей друг с другом. Уровни интеграционного тестирования: 64 Компонентный интеграционный уровень (Component Integration testing) проверяется взаимодействие между компонентами системы после проведения компонентного тестирования. Системный интеграционный уровень (System Integration Testing) проверяется взаимодействие между разными системами после проведения системного тестирования Подходы к интеграционному тестированию: ● Снизу вверх (Bottom Up Integration) Все низкоуровневые модули, процедуры или функции собираются воедино и затем тестируются. После чего собирается следующий уровень модулей для проведения интеграционного тестирования. Данный подход считается полезным, если все или практически все модули, разрабатываемого уровня, готовы. ● Сверху вниз (Top Down Integration) Вначале тестируются все высокоуровневые модули, и постепенно один за другим добавляются низкоуровневые. Все модули более низкого уровня симулируются заглушками с аналогичной функциональностью, затем по мере готовности они заменяются реальными активными компонентами. ● Большой взрыв (“Big Bang” Integration) Все или практически все разработанные модули собираются вместе в виде законченной системы или ее основной части, и затем проводится интеграционное тестирование. Подход “Big Bang” очень хорош для экономии времени. Но в тоже время при таком подходе довольно тяжело локализовывать ошибки. Регрессионное тестирование. Нагрузочное тестирование. Регрессионное тестирование (Regression Testing) - это вид тестирования направленный на проверку изменений, сделанных в приложении 65 или окружающей среде (починка дефекта, слияние кода, миграция на другую операционную систему, базу данных, веб-сервер или сервер приложения), для подтверждения того факта, что существующая ранее функциональность работает как и прежде. Регрессионное тестирование может выполняться на всех уровнях тестирования и включает функциональное, нефункциональное и структурное тестирование. Цель регрессионного тестирования – удостовериться в том, что существующая функциональность не была затронута изменениями в коде. А также, регрессионное тестирование нацелено на обнаружение ошибок, вызванных недавними изменениями в системе. В первую очередь надо проводить регрессионное тестирование: ● функций с высокой степенью риска; ● функций, которые чаще всего используются. Когда эти две категории проверены, можно проверять остальные функции и пути приложения. Нагрузочное тестирование или тестирование производительности это автоматизированное тестирование, имитирующее работу определенного количества бизнес пользователей на каком-либо общем (разделяемом ими) ресурсе. Основные виды нагрузочного тестирования: Тестирование производительности (Performance testing) Задачей тестирования производительности является определение масштабируемости приложения под нагрузкой, при этом происходит: измерение времени выполнения выбранных операций при определенных интенсивностях выполнения этих операций определение количества пользователей, одновременно работающих с приложением определение границ приемлемой производительности при увеличении нагрузки (при увеличении интенсивности выполнения этих операций) 66 исследование производительности на высоких, предельных, стрессовых нагрузках Стрессовое тестирование (Stress Testing) тестирование позволяет проверить насколько приложение и система в целом работоспособны в условиях стресса и также оценить способность системы к регенерации, т.е. к возвращению к нормальному состоянию после прекращения воздействия стресса. Стрессом в данном контексте может быть повышение интенсивности выполнения операций до очень высоких значений или аварийное изменение конфигурации сервера. Также одной из задач при стрессовом тестировании может быть оценка деградации производительности, таким образом цели стрессового тестирования могут пересекаться с целями тестирования производительности. Объемное тестирования тестирование является (Volume получение Testing). оценки Задачей объемного производительности при увеличении объемов данных в базе данных приложения, при этом происходит: измерение времени выполнения выбранных операций при определенных интенсивностях выполнения этих операций определение количества пользователей, одновременно работающих с приложением Тестирование стабильности или надежности (Stability / Reliability Testing). Задачей тестирования стабильности (надежности) является проверка работоспособности приложения при длительном (многочасовом) тестировании со средним уровнем нагрузки. Время выполнения операций может играть в данном виде тестирования второстепенную роль. При этом на первое место выходит отсутствие утечек памяти, перезапусков серверов под нагрузкой и другие аспекты влияющие именно на стабильность работы. 67 Стандарты оформления кода PEP 8 - руководство по написанию кода на Python. Этот документ описывает соглашение о том, как писать код для языка python, включая стандартную библиотеку, входящую в состав python. Используйте 4 пробела на каждый уровень отступа. Пробелы - самый предпочтительный метод отступов. Табуляция должна использоваться только для поддержки кода, написанного с отступами с помощью табуляции. Ограничьте длину строки максимум 79 символами. Предпочтительный способ переноса длинных строк является использование подразумеваемых продолжений строк Python внутри круглых, квадратных и фигурных скобок. Длинные строки могут быть разбиты на несколько строк, обернутые в скобки. Это предпочтительнее использования обратной косой черты для продолжения строки. Отделяйте функции верхнего уровня и определения классов двумя пустыми строками. Определения методов внутри класса разделяются одной пустой строкой. Дополнительные пустые строки возможно использовать для разделения различных групп похожих функций. В стандартной библиотеке, нестандартные кодировки должны использоваться только для целей тестирования, либо когда комментарий или строка документации требует упомянуть имя автора, содержащего не ASCII символы; в остальных случаях использование \x, \u, \U или \N наиболее предпочтительный способ включить не ASCII символы в строковых литералах. В стандартной библиотеке действует следующее соглашение: все идентификаторы обязаны содержать только ASCII символы, и означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские технические термины). Авторы, чьи имена основаны не на латинском алфавите, должны транслитерировать свои имена в латиницу. 68 Каждый импорт, как правило, должен быть на отдельной строке. Импорты должны быть сгруппированы в следующем порядке: 1. импорты из стандартной библиотеки 2. импорты сторонних библиотек 3. импорты модулей текущего проекта Указывайте спецификации __all__ после импортов. Всегда окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивания (=, +=, -= и другие), сравнения (==, <, >, !=, <>, <=, >=, in, not in, is, is not), логические (and, or, not). Если используются операторы с разными приоритетами, попробуйте добавить пробелы вокруг операторов с самым низким приоритетом. Комментарии должны являться законченными предложениями. Блок комментариев обычно объясняет код (весь, или только некоторую часть), идущий после блока, и должен иметь тот же отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа # и одного пробела после него (если только сам текст комментария не имеет отступа). Абзацы внутри блока комментариев разделяются строкой, состоящей из одного символа #. Имена, которые видны пользователю как часть общественного API должны следовать конвенциям, которые отражают использование, а не реализацию. Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать символы подчеркивания, если это улучшает читабельность. То же самое относится и к именам пакетов, однако в именах пакетов не рекомендуется использовать символ подчёркивания. Имена классов должны обычно следовать соглашению CapWords. Вместо этого могут использоваться соглашения для именования функций, если интерфейс документирован и используется в основном как функции. 69 Тема 10 Проведение изменений в ПО Обратный инжиниринг. Определение, цели проведения Под реверс-инжинирингом программного обеспечения понимается следующее: Обратная разработка (обратный инжиниринг, реверс-инжиниринг; англ. reverse engineering) — исследование некоторого устройства или программы, а также документации на них с целью понять принцип его работы и, чаще всего, воспроизвести устройство, программу или иной объект с аналогичными функциями, но без копирования как такового. Обратная инженерия (процесс систематического разбора программы (восстановления её исходного текста и структуры) или микросхемы для изучения алгоритмов её работы с целью имитации или повторения некоторых или всех её функций в другой форме или на более высоком уровне абстракции, снятия защиты, изучения алгоритмов, добавления новых возможностей, восстановления протоколов или исправления ошибок и др. Реверс-инжиниринг (Reverse Engineering) или, как его еще называют, обратная разработка, а иногда — обратное проектирование — это процесс анализа приложения для определения его функциональных характеристик, внутренней архитектуры и, собственно, его работы: модулей, функций, алгоритмов. Обобщая, можно утверждать, что реверс-инжиниринг (или обратная разработка) программного обеспечения представляет собой процесс исследования содержимого программы путем ее преобразования в исходный код для определения ее структуры и принципов ее работы. Цели проведения реинжиниринга заключаются в следующем: · получение представления о составе существующей системы; · моделирование существующей системы; · определение фрагментов программного кода, которые могут быть использованы в разрабатываемой новой системе; 70 · определение наследуемых данных. Задачи, решаемые при реинжиниринге, включают: · определение системных архитектур; · определение актеров существующей системы; · определение функциональности существующей системы; · определение логической структуры системы; · восстановление реляционной модели данных. Этапы обратного инжиниринга Начальная фаза. Фиксируется текущее состояние наследуемой системы (все изменения, вносимые в нее после этого момента, при выполнении реинжиниринга не учитываются). Определение системных архитектур. Определяются архитектуры БД, оборудования и стандартного ПО, телекоммуникаций. Все архитектуры представляются в нотации UML и при необходимости дополняются текстовыми описаниями. Автоматический инструментальных реинжиниринг средств осуществляется визуального с помощью моделирования. Ему подвергается как бизнес логика (если есть исходные коды на объектноориентированном или объектно-базированном языке), так и БД. Реинжиниринг БД выполняется с помощью инструментальных средств проектирования БД. Результатом является реляционная модель данных, которая может графически отображаться этим средством. Редактирование диаграмм моделей. Главной целью редактирования на этом этапе является достижение наглядности диаграмм. Метод повышения наглядности диаграмм хорошо известен. Это иерархическая реструктуризация. Средством ее осуществления в UML являются пакеты. Сложные ПС обычно включают несколько подсистем, имеющих разное целевое назначение и функциональность. 71 Построение функциональной модели. Модель функционирования описывается с помощью диаграмм ВИ и детализирующих их диаграмм последовательностей и деятельностей. Источником для ее построения является работающая наследуемая система и проводимые с ней эксперименты. Определение актеров. Для нахождения актеров следует искать ответы на следующие вопросы: · Кто является непосредственными пользователями системы? Необходимо при ответе на данный вопрос указывать роли, а не конкретных людей, исполняющих эти роли. Определение вариантов использования выполняется на основе анализа экранов работающей системы. Сначала определяются пакеты ВИ. Для этого следует найти все первичные экраны и для каждого из них в модели на головной диаграмме ВИ завести отдельный пакет. Таким образом, на этой диаграмме будет пакет актеров и несколько пакетов ВИ. Далее выполняется детализация построенных пакетов ВИ на основе анализа экранных форм. Определение взаимодействий актеров и ВИ. В модель включаются ассоциации. Они имеют направления, соответствующие направлениям передачи информации между актерами и ВИ. Распределение по пакетам. Если число актеров или ВИ слишком велико, то для упрощения поддержки модели ВИ целесообразно разделить их по пакетам. Построение навигации экранов. Одновременно с выделением ВИ строится навигация экранов наследуемой системы в виде диаграммы классов UML. Каждый экран показывается в модели как отдельный класс, в котором полям соответствуют атрибуты, функциональным кнопкам – операции, а кнопкам меню – одноименные отношения. Детализация функциональности представляет собой построение сценариев реализации ВИ, представленных в модели ВИ. Она 72 выполняется с помощью диаграмм последовательностей и диаграмм деятельностей UML. Анализ исходных данных Цель процесса анализа и проектирования состоит в разработке технических инструкций, предписывающих, как реализовать ПС, удовлетворяющую сформулированным требованиям. Для этого следует хорошо понять требования к ПС и преобразовать их в проект системы, выбрав правильную стратегию реализации. На ранних стадиях процесса должна быть создана устойчивая архитектура, на основе которой можно спроектировать ПС, легкую для понимания, построения и развертывания. Архитектура должна быть согласована со средой реализации с целью удовлетворения требований к производительности, устойчивости, безопасности, расширяемости и тестируемости. К числу решаемых задач при этом относятся: разработка точной архитектуры распределенной программной системы; преобразование модели требований в модель проектную разрабатываемой системы; адаптация проекта системы к среде реализации с целью повышения производительности разработки; выбор механизмов реализации и определение ограничений на реализацию; разработка компонентной структуры; распределение компонентов по узлам. Главной задачей анализа является преобразование требований в форму, понятную разработчику, то есть, определение подсистем, компонентов и классов, с помощью которых реализуется требуемое поведение ПС. В основе такого преобразования лежат ВИ, созданные при определении требований к ПС. При этом рассматриваются только функциональные требования и игнорируются нефункциональные. 73 В процессе анализа и проектирования создаются следующие документы: Модель проектирования – это основная модель ПС. Она описывает подсистемы, пакеты, компоненты, интерфейсы и классы, а также их взаимодействия, обеспечивающие требуемое поведение ПС. Документ «Архитектура ПС», в котором собраны различные архитектурные представления ПС. Модель данных – это описание структуры данных, хранимых в БД (например, реляционная модель данных). Получение экспертиз. Реверсивный инжиниринг Исследование и обратная разработка программ обычно осуществляются с целью дальнейшей модификации, копирования, или, например, написания генераторов ключей, алгоритм работы которых получен на основе анализа алгоритма их проверки. Также исследование программ применяется с целью получения некоторых закрытых сведений о внутреннем устройстве программы — о протоколе сетевого обмена с сервером, аппаратным средством, ключом защиты или о взаимодействии с другой программой. Ещё одна область применения — получение информации о способах экспортирования данных из многочисленных проприетарных форматов файлов. Такой способ наиболее приемлем в тех случаях, когда создатель первоначальной программы скрыл или не предоставил информацию о производстве конкретного объекта. Осуществляет такой инжиниринг специалист, имеющий квалификацию: «программист» или «инженер компьютерных технологий». Способ широко применяется в производстве, бизнесе, различных отраслях промышленности. Первоначальная программа, переделанная в соответствии с новыми усовершенствованиями, позволяет добиться наилучших результатов при 74 изготовлении готовой продукции и запасных частей к различным устройствам. Инжиниринг использует при этом: Машинный анализ кода. Составление алгоритма. Выработку кода. Выделяет особенности и спецификации. Вносит усовершенствования. Использует некоторые элементы идентичности. Заменяет драйвер собственным. С развитием Интернета популярные операционные системы и программы всё интенсивнее исследуются на предмет обнаружения в них уязвимостей или т. н. «дыр». В дальнейшем найденные дыры могут использоваться для получения несанкционированного доступа к удалённому компьютеру или компьютерной сети. C другой стороны, обратная разработка применяется при исследовании антивирусными компаниями вредоносного ПО c целью добавления его сигнатур в базы своих продуктов. В настоящее время под словами «reverse engineering» чаще всего понимается т. н. clean room reverse engineering, то есть процесс, при котором одна группа разработчиков анализирует машинный код программы, составляет алгоритм данной программы на псевдокоде либо, если программа является драйвером какого-либо устройства, составляет исчерпывающие спецификации интересующего устройства. После получения спецификаций другая группа разработчиков пишет собственный драйвер на основе полученных спецификаций или алгоритмов. Такой подход позволяет избежать обвинений в нарушении авторских прав на исходную программу. Результат обратной разработки редко идентичен оригиналу, что и позволяет избежать ответственности перед законом, особенно при условии контроля отсутствия 75 этой идентичности первой группой разработчиков и отсутствия нарушений торговых марок и патентов. Методики проведения обратного инжиниринга Обратная разработка программного обеспечения основана на следующих способах, применяющихся в производстве и бизнес-сфере: Постоянный обменный анализ данных любого протокола. Здесь используют анализатор шины, а также пакетный сниффер. Дизассемблирование – метод считывания прямого кода машины и используемого языка. Применяется к любой компьютерной программе. Считается длительным процессом. Декомпиляция – создание ключевого кода программы с помощью определенного языка программирования. Язык выбирает инженер- программист. Использование первоначальной базы данных. Это позволяет создать конкретную модель. Аналитика исходного кода и создание нужной программы предоставляют возможность восстановления исходного кода проекта, который был первоначальным. Это позволяет образовать связи между классами и блоками различных бизнес-процессов. Реверс часто применяется в отношении форматов, которые поддерживаются программой Microsoft Office. В ряде инструментов UML процесс импорта и анализа исходного кода для создания диаграмм UML называется «обратным проектированием». Хотя UML является одним из подходов к обеспечению «обратного проектирования», недавние достижения в области международных стандартов привели к разработке Метамодели обнаружения знаний (KDM). Стандарт предоставляет онтологию для промежуточного (или абстрактного) представления конструкций языка программирования и их взаимосвязей. Стандарт группы управления объектами (который также становится 76 стандартом ISO), KDM начал завоевывать промышленность с помощью разработки инструментов и сред анализа, которые могут обеспечивать извлечение и анализ источника, двоичный и байтовый код. Для анализа исходного кода архитектура гранулярных стандартов KDM позволяет извлекать потоки программной системы (данные, управление и карты вызовов), архитектуры и знания бизнес-уровня (правила, термины и процессы). Стандарт позволяет использовать общий формат данных (XMI), позволяющий коррелировать различные уровни системных знаний для детального анализа (например, первопричина, влияние) или производного анализа (например, извлечения бизнес-процессов). Хотя усилия по представлению языковых конструкций могут быть бесконечными из-за количества языков, непрерывной эволюции языков программного обеспечения и разработки новых языков, стандарт все же позволяет использовать расширения для поддержки широкого набора языков, а также эволюция. KDM совместим с UML, BPMN, RDF и другими стандартами, что позволяет выполнять миграцию в другие среды и, таким образом, использовать системные знания для таких усилий, как преобразование программных систем и анализ уровня предприятия. Оформление кода Стандарт оформления кода (англ. coding standards, coding convention или programming style) — набор правил и соглашений, используемых при написании исходного кода на некотором языке программирования. Наличие общего стиля программирования облегчает понимание и поддержание исходного кода, написанного более чем одним программистом, а также упрощает взаимодействие нескольких человек при разработке программного обеспечения. Стандарт сильно зависит от используемого языка программирования. В целом, исходя из назначения стандарта, обычно он имеет целью добиться 77 такого положения, когда программист достаточной квалификации мог бы дать заключение о функции, выполняемой конкретным участком кода, а в идеале — также определить его корректность, изучив только сам этот участок кода или, во всяком случае, минимально изучив другие части программы. Иными словами, смысл кода должен быть виден из самого кода, без необходимости изучать контекст. Поэтому стандарт кодирования обычно строится так, чтобы за счёт определённого визуального оформления элементов программы повысить информативность кода для человека. Обычно, стандарт оформления кода описывает: способы выбора названий и используемый регистр символов для имён переменных и других идентификаторов: запись типа переменной в её идентификаторе (венгерская нотация) и регистр символов (нижний, верхний, «верблюжий», «верблюжий» с малой буквы), использование знаков подчёркивания для разделения слов; стиль отступов при оформлении логических блоков — используются ли символы табуляции, ширина отступа; способ расстановки скобок, ограничивающих логические блоки; использование пробелов при оформлении логических и арифметических выражений; стиль комментариев и использование документирующих комментариев. Основные принципы распространённых стандартов кодирования в последнее время оказывают влияние на синтаксис вновь создаваемых языков программирования. В некоторых из них соглашения, ранее применявшиеся только в стандартах кодирования, стали обязательными элементами синтаксиса. В результате, если ранее недисциплинированный программист мог игнорировать стандарты кодирования из личных соображений, ради удобства или скорости написания кода, то теперь, при работе на новых языках, соблюдение стандартов в определённой мере контролируется транслятором. 78 Реинжиниринг Реинжиниринг – это процесс создания нового или преобразования существующего ПО поддерживаемой с им целью улучшения функциональности, характеристик качества, понижения стоимости сопровождения, уменьшения вероятности возникновения значимых для заказчика рисков. Реинжиниринг подразумевает использование уже имеющегося в эксплуатации у заказчика программного обеспечения в качестве базиса, основы для работы. Реинжиниринг включает в себя следующие виды работ: анализ исходного ПО; изучение и анализ исходного кода (если применимо); анализ слабых мест исходного ПО; исследование возможностей исходного ПО и подробное их документирование восстановление архитектуры исходного ПО; анализ рисков и выработка шагов к их нейтрализации; составление технического задания на новое ПО; трансформация восстановленной архитектуры к желаемой новой архитектуре; тестирование нового ПО; установка и настройка нового ПО; конвертация накопленных данных на новое ПО; документирование новой системы; обучение персонала (если необходимо). Реинжиниринг имеет несомненные достоинства для заказчика, использующего наработанный годами опыт, бизнес-логику, а также функционал автоматизирующий уникальные процессы предприятия. 79 Для исполнителя же реинжиниринг является довольно сложной задачей, требующей высокой квалификации разработчиков. Это связано со следующими проблемами: Необходимость разбора чужого исходного кода. Такую задачу не может сделать программист низкой и средней квалификации. Даже профессионалы часто не могут качественно реализовать его. Поэтому требуется работа специалистов с большим опытом и знанием различных технологий. Необходимость расширения границ существующего функционала, но при этом обязательная совместимость с предыдущими версиями. Риски изменения функционала «в живую» – на работающем предприятии, в режиме реального времени. Именно поэтому стоимость реинжиниринга зачастую выше стоимости разработки с нуля. В то же время, если изначально программа обладала строгой и ясной архитектурой, то провести реинжиниринг будет проще. Поэтому при проектировании, как правило, анализируется, что выгоднее провести реинжиниринг, или разработать программный продукт "с нуля". Анализ интерфейса Графический интерфейс пользователя (Graphical user interface, GUI) – разновидность пользовательского интерфейса, в котором элементы интерфейса (меню, кнопки, значки, списки и т. п.), представленные пользователю на дисплее, исполнены в виде графических изображений. Проверяется в целом общий вид приложения и в отдельности формы, расположенные на странице. Общие проверки: • Вид элементов при уменьшении окна браузера + появление скрола • Правильность написания текста + текст должен быть выровнен • Правильность перемещения фокуса в окне (Tab / Tab+Shift) 80 • Выбранные элементы выделяются • Неизменяемые поля выглядят одинаково и отличаются от редактируемых • Желательно не использовать двойной клик • Проверка наличия нужных нотификейшенов • Унификация дизайна (цвет, шрифт, размер) • При необходимости должны быть тултипы • Изменение вида элемента при ховере на него • Если формы дублируются, то должны быть одинаковые названия • Дополнительные проверки для веб-форм Основные моменты при проверке GUI: • расположение, размер, цвет, ширина, длина элементов; возможность ввода букв или цифр • реализуется ли функционал приложения с помощью графических элементов • размещение всех сообщений об ошибках, уведомленией (а также шрифт, цвет, размер, расположение и орфография текста) • читабелен ли использованный шрифт • переходит ли курсор из текстового в поинтер при наведении на активные элементы, выделяются ли выбранные элементы • выравнивание текста и форм • качество изображений • проверить расположение и отображение всех элементов при различных разрешениях экрана, а также при изменении размера окна браузера (проверить, появляется ли скролл) • проверить текст на орфографические, пунктуационные ошибки • появляются ли тултипы (если есть необходимость) • унификация дизайна (цвета, шрифты, текст сообщений, названия кнопок и т.д.) Наиболее распространенные баги: 81 • курсор не переходит в поинтер при наведении на активный элемент • орфографические и грамматические ошибки • не ровное расположение полей ввода в формах, самих форм • неправильное отображение элементов при смене размера окна браузера и масштаба страницы • изменение размера текста при смене языка • неровное расположение форм • разные шрифты • выбранные элементы не отличаются от не выбранных 82 Список использованных источников 1. Орлов С.А., Цилькер Б.Я. Технологии разработки программного обеспечения: Учебник для вузов./С.А.Орлов, Б.Я.Цилькер- 4-е изд. Стандарт третьего поколения. – СПб.: Питер, 2018 – 608 с. 2. Златопольский Д.М. Основы программирования на языке Python./Д.М.Златопольский – М.: ДМК Пресс, 2017. – 284 с. 3. Гэддис Т. Начинаем программировать на Python. /Т.Геддис – 4-е изд.: Пер. с англ. – СПб.: БХВ-Петербург, 2019. – 768 с. 4. Лучано Рамальо Python. К вершинам мастерства./Л.Ромальо – М.: ДМК Пресс, 2016. – 768 с. 5. Свейгарт, Эл. Автоматизация рутиных задач с помощью Python: практическое руководство для начинающих./Эл.Цвейгарт- Пер. с англ. — М.: Вильямc, 2016. – 592 с. 6. Рейтц К., Шлюссер Т. Автостопом по Python. /К.Рейтц, Т.Шлюссер – СПб.: Питер, 2017. – 336 с.: ил. – (Серия «Бестселлеры O’Reilly»). 7. Любанович Билл Простой Python. Современный стиль программирования. /Б.Лобанович– СПб.: Питер, 2016. – 480 с.: – (Серия «Бестсепперы O’Reilly»). 8. Павловская Т.А. С#. Программирование на языке высокого уровня. /Т.А.Павловская-СПб.: Питер, 2014 9. Федоров, Д. Ю. Программирование на языке высокого уровня Python : учебное пособие для прикладного бакалавриата / Д. Ю. Федоров. – 2-е изд., перераб. и доп. – Москва : Издательство Юрайт, 2019. – 161 с. – (Бакалавр. Прикладной курс). – ISBN 978-5-534-10971-9. – Текст: электронный // ЭБС Юрайт [сайт]. – URL: https://urait.ru/bcode/437489 (дата обращения: 13.02.2020). 10.Шелудько, В. М. Основы программирования на языке высокого уровня Python: учебное пособие / В. М. Шелудько. – Ростов-на-Дону, Таганрог: 83 Издательство Южного федерального университета, 2017. – 146 c. – ISBN 978-5-9275-2649-9. – Текст: электронный // Электронно-библиотечная система IPR BOOKS: [сайт]. – URL: http://www.iprbookshop.ru/87461.html (дата обращения: 13.02.2020). – Режим доступа: для авторизир. пользователей 84
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )