[TOC]
----------------------------------------------------------------------------------------------------
# Задачи проектирования
В процессе **проектирования** программы определяются основные потребительские свойства этой
программы(*интерфейс* и функционал), а также в общем виде структурные элементы и способы реализации
(*моделирование* и *декомпозиция*).
## Интерфейс {#interface}
**Интерфейс** определяет способ взаимодействия с программой. Например, это может быть веб-страница
или консольное приложение. Интерфейс определяет какие данные пользователь вводит в программу, а
какие получает в качестве результатов, а также определяет способы управления программой.
Данный вид интерфейса называется **пользовательским** (UI, user's interface) и может быть:
- **Графическим интерфейсом** (GUI, graphic UI), это форма пользовательского интерфейса, которая
позволяет пользователям взаимодействовать с устройством с помощью графических элементов (значков,
курсоров, кнопок).
- **Интерфейсом командной строки** (CLI, command line interface), позволяет управлять работой программ
посредством ввода команд и получать информацию в текстовом виде. Такие программы часто
называются *консольными*.

Программа также может взаимодействовать с другой программой. Способ взаимодействия программ
часто называется *протоколом*. Протокол можно считать элементом **программного интерфейса**.
На рисунке изображены различные программы с графическим пользовательским интерфейсом (это могут
быть браузер, файловый менеджер, например, Проводник 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
````