Использование неизменяемых данных с ненадёжным кодом #
Презентация #
Презентацию по теме лекции можно открыть по ссылке.
Видео-запись: Rutube
Документация #
- 7 глава книги Grokking Simplicity, Staying immutable with untrusted code
- JavaScript глубокое и поверхностное копирование
- Поверхностное и глубокое копирование в C++
- Глубокое и поверхностное копирование в Python
Задания #
Задания выполняются на языке JavaScript в стиле лекции. Для каждого решения приведите код и краткое обоснование выбранной техники: копирование при записи или защитное копирование. Справочник по используемым методам: structuredClone, Object, Array.
-
Реализация глубокого копирования. Расширьте учебную реализацию
deepCopyиз лекции так, чтобы она корректно копировала объектыDate, а также массивы и объекты произвольной глубины вложения:var order = { id: 42, created: new Date(2026, 0, 15), items: [{name: "shoes", tags: ["sale", "new"]}] }; var copy = customDeepCopy(order); copy.items[0].tags.push("gift"); console.log(order.items[0].tags.length); // ожидается 2 console.log(copy.items[0].tags.length); // ожидается 3 console.log(copy.created instanceof Date); // ожидается trueОтдельно объясните письменно, почему функции при таком копировании остаются общими для оригинала и копии и к каким последствиям это приводит.
-
Универсальная обёртка защитного копирования. Реализуйте функцию высшего порядка
protect(fn), которая возвращает безопасную версию ненадёжной функции:var payrollCalcSafe = protect(payrollCalc); var checks = payrollCalcSafe(employees); var blackFridaySafe = protect(black_friday_promotion); shopping_cart = blackFridaySafe(shopping_cart);Обёртка должна поддерживать две сигнатуры: функция-вычисление возвращает значение, функция-действие ничего не возвращает, но изменяет аргумент. Проверьте обе на функциях, которые изменяют переданный массив и изменяют вложенный объект внутри него.
-
Защита от ненадёжной библиотеки событий. Унаследованный модуль котировок передаёт один и тот же объект всем подписчикам:
ticker.onUpdate(function (quote) { render_quote(quote); history.push(quote); });Перепишите обработчик так, чтобы в
historyхранились независимые снимки котировок, а изменение одного снимка другим подписчиком не влияло на остальные. Добавьте функциюget_history(), которая возвращает историю без риска испортить внутренние данные со стороны вызывающего кода, и объясните, какое из двух правил защитного копирования применено в каждом месте. -
Аудит унаследованного кода. Для каждой функции определите, требуется ли защитное копирование, и если да — при передаче, при получении или в обоих местах. Объясните, что произойдёт без копирования.
// A function normalize_names(users) { var copy = []; for (var i = 0; i < users.length; i++) { copy.push({name: users[i].name.trim()}); } return copy; } // B function fill_defaults(user) { if (!user.role) user.role = "guest"; return user; } // C var roles = ["guest", "admin"]; function get_roles() { return roles; } -
Поиск ошибок защитного копирования. Для каждого фрагмента определите, сохраняет ли он неизменяемость данных на границе с ненадёжным кодом. Ошибочные варианты исправьте и объясните причину ошибки.
// A function call_legacy(cart) { var copy = cart.slice(); legacy_promo(copy); return copy; } // B function call_legacy(cart) { var copy = customDeepCopy(cart); return legacy_promo(copy); } // C function call_legacy(cart) { return customDeepCopy(legacy_promo(cart)); } // D function call_legacy(cart) { var copy = customDeepCopy(cart); legacy_promo(copy); return customDeepCopy(copy); } -
Выбор техники для проекта. Для каждой ситуации укажите подходящую технику и обоснуйте решение, включая стоимость применения:
- функция внутри доверенной зоны меняет поле вложенного объекта корзины;
- данные о заказах приходят из унаследованной библиотеки, которую нельзя изменить;
- обработчик передаёт корзину во внешний модуль расчёта скидок, который изменяет её содержимое;
- унаследованный модуль планируется переписать на копировании при записи в следующем спринте, но интеграция нужна сейчас.