HTML Diff
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>