0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>Конспект урока</strong></p>
1
<p><strong>Конспект урока</strong></p>
2
<h2>Проблема: код не поддаётся тестированию</h2>
2
<h2>Проблема: код не поддаётся тестированию</h2>
3
<p>Наша игра работает, но обладает ограничением - мы не можем её полноценно тестировать.</p>
3
<p>Наша игра работает, но обладает ограничением - мы не можем её полноценно тестировать.</p>
4
<p>Если колода содержит две и более карты, то ход игры и её результат заранее определить и протестировать НЕ получится.</p>
4
<p>Если колода содержит две и более карты, то ход игры и её результат заранее определить и протестировать НЕ получится.</p>
5
<p>Ведь в процессе игры карта выбирается случайным образом:</p>
5
<p>Ведь в процессе игры карта выбирается случайным образом:</p>
6
<p>Вызов random(cards) возвращает случайную карту. Этот код располжен внутри функции с игрой, поэтому делает недетерминированной всю игру.</p>
6
<p>Вызов random(cards) возвращает случайную карту. Этот код располжен внутри функции с игрой, поэтому делает недетерминированной всю игру.</p>
7
<h2>Решение: инвертирование</h2>
7
<h2>Решение: инвертирование</h2>
8
<p>Сейчас выбор карты осуществляется внутри игры, и мы не можем на это никак повлиять. Но ситуация изменится, если сделать так, чтобы алгоритм выбора карты игра получала "снаружи". Это легко реализовать с помощью передачи параметра.</p>
8
<p>Сейчас выбор карты осуществляется внутри игры, и мы не можем на это никак повлиять. Но ситуация изменится, если сделать так, чтобы алгоритм выбора карты игра получала "снаружи". Это легко реализовать с помощью передачи параметра.</p>
9
<p>Рассмотрим простой пример: функция принимает на вход колоду карт cards, внутри происходит случайный выбор карты и какие-то другие дальнейшие манипуляции. Это принципиальная схема, если отвлечься от несущественных деталей.</p>
9
<p>Рассмотрим простой пример: функция принимает на вход колоду карт cards, внутри происходит случайный выбор карты и какие-то другие дальнейшие манипуляции. Это принципиальная схема, если отвлечься от несущественных деталей.</p>
10
<p>Эта функция НЕ является чистой, она недетерминирована. И это нормально для игры, но не для тестов.</p>
10
<p>Эта функция НЕ является чистой, она недетерминирована. И это нормально для игры, но не для тестов.</p>
11
<p>Применим к функции технику инвертирования, реализовав передачу процесса выбора карты снаружи, через параметры:</p>
11
<p>Применим к функции технику инвертирования, реализовав передачу процесса выбора карты снаружи, через параметры:</p>
12
<p>Теперь мы можем управлять процессом выбора карты, передавая (в зависимости от ситуации и наших целей) ту или иную функцию в параметр customRandom.</p>
12
<p>Теперь мы можем управлять процессом выбора карты, передавая (в зависимости от ситуации и наших целей) ту или иную функцию в параметр customRandom.</p>
13
<h2>Тесты</h2>
13
<h2>Тесты</h2>
14
<p>Для тестирования нам не подойдёт обычный random. Поэтому определим и передадим в функцию игры специальную функцию выбора карты, обеспечивающую предсказуемое поведение:</p>
14
<p>Для тестирования нам не подойдёт обычный random. Поэтому определим и передадим в функцию игры специальную функцию выбора карты, обеспечивающую предсказуемое поведение:</p>
15
<p>Мы передали в игру (вторым параметром) анонимную функцию:</p>
15
<p>Мы передали в игру (вторым параметром) анонимную функцию:</p>
16
<p>Её ядро заключается в строчке кода, определяющей текущий индекс:</p>
16
<p>Её ядро заключается в строчке кода, определяющей текущий индекс:</p>
17
<p>Значение переменной cardIndex функция берёт из переменной, определённой во внешнем окружении:</p>
17
<p>Значение переменной cardIndex функция берёт из переменной, определённой во внешнем окружении:</p>
18
<p>Это важно, так мы можем смоделировать нужное нам предсказуемое поведение. При каждом новом вызове значение cardIndex циклически меняется с нуля на единицу и наоборот (индексирование в списке карт начинается с нуля!). Это как раз то, что нужно для ситуации колоды, состоящей из двух карт.</p>
18
<p>Это важно, так мы можем смоделировать нужное нам предсказуемое поведение. При каждом новом вызове значение cardIndex циклически меняется с нуля на единицу и наоборот (индексирование в списке карт начинается с нуля!). Это как раз то, что нужно для ситуации колоды, состоящей из двух карт.</p>
19
<p><em>Обязательно проанализируйте процесс выбора карт в модуле с тестами в практике к этому уроку!</em></p>
19
<p><em>Обязательно проанализируйте процесс выбора карт в модуле с тестами в практике к этому уроку!</em></p>
20
<p>На примере простой функции продемонстрируем принцип определения циклического (а значит предсказуемого!) изменения величины:</p>
20
<p>На примере простой функции продемонстрируем принцип определения циклического (а значит предсказуемого!) изменения величины:</p>
21
<p>Для колоды из трёх или какого-нибудь другого количества карт надо будет модифицировать функцию. Можно сразу написать более универсальный вариант:</p>
21
<p>Для колоды из трёх или какого-нибудь другого количества карт надо будет модифицировать функцию. Можно сразу написать более универсальный вариант:</p>
22
<h2>Выводы</h2>
22
<h2>Выводы</h2>
23
<p>С помощью техники инвертирования мы добились следующих преимуществ:</p>
23
<p>С помощью техники инвертирования мы добились следующих преимуществ:</p>
24
<ul><li>Предсказуемого поведения - код стало возможным тестировать. Теперь можем управлять процессом выбора в зависимости от целей: для игр передавать обычный random, для тестов - кастомный.</li>
24
<ul><li>Предсказуемого поведения - код стало возможным тестировать. Теперь можем управлять процессом выбора в зависимости от целей: для игр передавать обычный random, для тестов - кастомный.</li>
25
<li>В целом, добились расширения возможностей программы посредством делегирования части функциональности внешнему коду. Программа стала более гибкой в использовании.</li>
25
<li>В целом, добились расширения возможностей программы посредством делегирования части функциональности внешнему коду. Программа стала более гибкой в использовании.</li>
26
</ul>
26
</ul>