[TOC] ---------------------------------------------------------------------------------------------------- # Задачи проектирования В процессе **проектирования** программы определяются основные потребительские свойства этой программы(*интерфейс* и функционал), а также в общем виде структурные элементы и способы реализации (*моделирование* и *декомпозиция*). ## Интерфейс {#interface} **Интерфейс** определяет способ взаимодействия с программой. Например, это может быть веб-страница или консольное приложение. Интерфейс определяет какие данные пользователь вводит в программу, а какие получает в качестве результатов, а также определяет способы управления программой. Данный вид интерфейса называется **пользовательским** (UI, user's interface) и может быть: - **Графическим интерфейсом** (GUI, graphic UI), это форма пользовательского интерфейса, которая позволяет пользователям взаимодействовать с устройством с помощью графических элементов (значков, курсоров, кнопок). - **Интерфейсом командной строки** (CLI, command line interface), позволяет управлять работой программ посредством ввода команд и получать информацию в текстовом виде. Такие программы часто называются *консольными*. ![](interfaces_overview.png) Программа также может взаимодействовать с другой программой. Способ взаимодействия программ часто называется *протоколом*. Протокол можно считать элементом **программного интерфейса**. На рисунке изображены различные программы с графическим пользовательским интерфейсом (это могут быть браузер, файловый менеджер, например, Проводник Windows, текстовый редактор, например Блокнот). Также приведены примеры консольных программ: интерпретатора Python IDLE и системы контроля версий Git. Из программных протоколов упомянуты: - HTTP(S) --- протокол взаимодействия браузера с сервером (также сервера могут использовать этот протокол для взаимодействия друг с другом); - POP/SMTP/IMAP --- набор протоколов, используемых в системе электронной почты. Таким образом получается, что пользователь взаимодействует с программой, например браузером, используя манипуляции с графическим интерфейсом, а браузер отправляет запросы на сервер и получает ответы используя программный протокол, например HTTPS. В случае с консольной программой ситуация аналогичная. Например, пользователь системы контроля версий Git выполняет в консоли команду `git push`, после чего программа Git отправляет локальные изменения в удаленный репозиторий (например, GitHub), используя специализированный программный протокол. Рассмотренные выше понятие и разновидности интерфейсов трактуют интерфейс достаточно широко. С точки зрения проектирования мы чаще будем рассматривать интерфейс обособленного фрагмента программы, т.е. [*функции*](#function). Интерфейсом функции является набор принимаемых аргументов и тип возвращаемого значения. Интерфейс функции определяет способ взаимодействия этой функции с другим программным кодом. Интерфейс не определяет способ реализации функционала программы (или фрагмента программы). ## Моделирование **Модель** --- это такое представление объекта реального мира, которое содержит минимальное количество его характеристик, необходимое для решения поставленной задачи. Например: для библиотеки читатель представляется как сущность имеющая атрибуты ФИО, адрес, паспортные данные. Такие характеристики как семейное положение и привычки не требуются. ## Декомпозиция **Декомпозиция** --- это способ управления сложностью задачи, при котором задача разбивается на подзадачи, которые в свою очередь могут также разбиваться образуя иерархию. Декомпозицию стоит выполнять для задач, которые сложно понять и решить без разбиения. Подзадачи следует стараться выделять максимально логически завершенными с минимальной зависимостью от остальных задач. В этом случае возможно найти уже существующие решения, либо созданное решение может быть в последствии использовано в других задачах. ---------------------------------------------------------------------------------------------------- # Пример проектирования Дана функция [`hanoi`](#hanoi), которая возвращает последовательность перекладывания Ханойской башни. **Задача:** протестировать эту функцию. ## Интерфейс Функцию тестирования назовем `test_hanoi`. Она будет принимать те же аргументы (`n, s1, s3, s2`), что и функция `hanoi`и проверять работу функции `hanoi` с заданными аргументами. При положительном результате (функция `hanoi` работает корректно), функция `test_hanoi` ничего не выводит. В противном случае выводит сообщение об ошибке, включающее перечисление аргументов и описание некорректного поведения. ## Моделирование Проверку будем осуществлять посредством эмуляции перекладывания дисков реальных Ханойских башен. Стержни будут представлены массивами, а диски элементами массивов. Диск у основания пирамиды будет представлен элементом массива по индексу 0. Перекладывание будет смоделировано извлечением (удалением) последнего элемента одного массива и добавлением этого элемента в конец другого массива. Упомянутые выше массивы удобно расположить в массиве из трех элементов (`[[], [], []]`) и обращаться к ним по индексу. Для этих целей введём требование, что аргументы `s1, s2, s3` могут принимать значения 0, 1 или 2. !!! note "Замечание" Последнее соображение на самом деле к моделированию не относится, а относится к реализации. Здесь приведено для удобства последующего описания декомпозиции. ## Декомпозиция Весь процесс реализации удобно разделить на следующие подзадачи: 1. Собираем проверочный стенд. Заполняем массив с индексом `s1` числами от `n` до 1 и в убывающем порядке. 1. Проверяем процесс перекладывания. Предполагаемый алгоритм. Вызываем функцию `hanoi`, получаем от нее последовательность перекладываний, выполняем перекладывания и на каждом шаге проверяем, что добавляемый элемент меньше предыдущего, либо является единственным элементом массива, если это не так, то выводим сообщение об ошибке и завершаем проверку. 1. Проверяем конечное состояние. По окончании перекладываний проверим, что в массиве с индексом `s3` находится `n` элементов, а два других массива пусты. Реализация теста представлена [здесь](#hanoi). ----------------------------------------------------------------------------------------------------- # Принцип DRY Википедия: [Don’t repeat yourself](https://ru.wikipedia.org/wiki/Don%E2%80%99t_repeat_yourself): > Принцип DRY формулируется как: «Каждая часть знания должна иметь единственное, непротиворечивое > и авторитетное представление в рамках системы». > > ... > > Когда принцип DRY применяется успешно, изменение единственного элемента системы не требует > внесения изменений в другие, логически не связанные элементы. Примером следования этому принципу является использование *функций*. Если имеется фрагмент программного кода, решающий определенную задачу, то можно добавлять его в те места программы, где он необходим. Однако модификация такого кода потребуется во всех местах, где он был применен. Это очень сложно или даже невозможно. Выделив данный фрагмент кода в функцию, мы избегаем повторения. Код становится более управляемым и к тому же более читабельным. Аббревиатура **DRY** расшифровывается как **D**on't **R**epeat **Y**ourself --- не повторяйся. К тому же слово "dry" означает сухой. Программисты иногда говорят: "подсушить код", т.е. вынести повторяющийся код в отдельные функции. ----------------------------------------------------------------------------------------------------- # Объектно-ориентированное программирование Википедия: [Объектно-ориентированное программирование, (*англ.*) Object Oriented Programming (ООР)]( https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5): > Объе́ктно-ориенти́рованное программи́рование (сокр. *ООП*) — методология программирования, основанная > на представлении программы в виде совокупности взаимодействующих объектов, каждый из которых > является экземпляром определённого класса, а классы образуют иерархию наследования. Технические детали создания объектов и классов представлены в разделе ["Объекты и классы"](#objects_and_classes). Здесь будут рассмотрены более теоретические вопросы. *Типы данных* в различных языках используются для определения способа интерпретации значений. Например: имея ссылку на целое число, транслятор языка руководствуясь описанием типов, принимает решение сколько ячеек памяти следует прочитать и каким образом выполнять операции с этим значением. **Классы** можно представить как типы данные созданные программистом. Так же как с другими типами для использования класса требуется создать соответствующие значения, **экземпляры** класса. Механизм **наследования** классов в какой-то степени аналогичен наследованию признаков живых существ от родителей к потомкам в природе. В случае с классами дочерний класс обретает (наследует) все или часть полей и методов родительского класса. Особенностью языка Python является отсутствие требования соответствия объекта определенному типу (классу). В более строгих языках программирования принадлежность объектов определенному классу определяет возможность его использования в конкретной ситуации, несмотря на это в языке Python является очень важной идея *интерфейса*. **Интерфейс** определяет поведение реализующих его объектов, однако сам это поведение не реализует. Рассмотрим интерфейс [итерируемого объекта]( #iterable_objects), который может включать два метода: - сообщить о наличии следующего элемента; - вернуть следующий элемент. Данный интерфейс может быть реализован различными объектами например, *коллекциями* и *генераторами*. Цикл `for` принимает итерируемый объект, который реализует перечисленные выше методы, следовательно цикл `for` может работать одинаковым образом с различными объектами, если они реализуют требуемый интерфейс. !!! Note На самом деле сообщение об отсутствии следующего элемента происходит с использованием [механизма исключений](#exceptions). Но в контексте данного повествования эта особенность не существенна. Один объект может реализовывать более одного интерфейса, например рассмотрим *списки* и *словари*. Для доступа к элементам этих коллекций используются квадратные скобки, то же самое можно делать и со строками. Поддержка такого способа доступа также является реализацией интерфейса (subscriptable). Объекты не реализующие этот интерфейс (например, число) такой способ доступа не поддерживают. ````shell >>> n=5 >>> n[0] TypeError: 'int' object is not subscriptable ````