3. Жизненный цикл любого проекта
Ваш
проект
встроен
в
контекст
и
взаимодействует
с
окружением
4. Жизненный цикл любого проекта
Однако
ваш
контекст
нестабилен:
меняется
платформа,
железо,
вкусы
пользователей…
5. Жизненный цикл любого проекта
В
какой-‐то
момент
вам
приходится
чинить
то,
что
сломалось
из-‐за
изменений
в
окружающей
среде.
Чем
больше
проект,
чем
нестабильнее
контекст
–
тем
больше
затраты
на
поддержку.
6. В один прекрасный день всё меняется
Представьте,
что
вы
сами
стали
предоставлять
API
для
сторонних
проектов.
И
они
почему-‐то
перестали
радоваться
вашим
рефакторингам
и
оптимизациям
7. Обратная совместимость
Обратная
совместимость
означает,
что
единожды
принятое
обязательство
предоставлять
какую-‐либо
функциональность
невозможно
отменить,
исправить
или
перестать
поддерживать.
8. Обратная совместимость: бочка мёда
• Вы
экономите
время
и
деньги
своим
пользователям
• Ваша
карма
остаётся
мягкой
и
шелковистой
• Сервис,
использующий
ваше
API,
не
сломается
в
продакшене
• Это,
чёрт
возьми,
клиенториентированно!
9. Обратная совместимость: две бочки дёгтя
• Вы
будете
писать
больше
кода
• Вам
придётся
гораздо
больше
думать
• У
вас
будет
гораздо
больше
интерфейсов,
сущностей,
накладных
расходов
и
избыточных
конструкций
10. Обратная совместимость: что нужно знать
Предполагается,
что
вы
уже
читали
"Совершенный
код"
и
умеете
проектировать
системы.
Ваш
код
должен
быть
разделён
по
уровням
абстракции.
Пока
это
не
так
–
даже
не
начинайте.
Изображение
Медиатека
Файл
HDD
EXT
Директория
SSD
HFS
Программа
NTFS
Network
SMB
NFS
11. Обратная совместимость: что нужно знать
Кроме
того,
вы
должны
чётко
понимать
границы
своей
ответственности:
• То,
что
однажды
было
открыто,
закрыть
уже
нельзя;
• То,
что
по
вашему
недосмотру
попало
в
публичную
область
–
останется
там
навечно;
• Если
из
публичного
интерфейса
можно
получить
доступ
к
непубличным
объектам
–
считайте,
что
вы
их
открыли.
12. Правило №1: больше интерфейсов
• В
пределе
в
вашей
публичной
документации
не
должно
быть
ни
одной
сигнатуры,
принимающей
конкретные
типы,
а
не
интерфейсы
• Исключение
может
быть
сделано
для
базовых
глобальных
классов
и
явно
подчинённых
компонент
14. Правило №1: больше интерфейсов
Почему
это
помогает
избежать
потери
обратной
совместимости:
• Если
в
сигнатуре
заявлен
интерфейс,
у
вас
не
будет
проблем,
когда
у
вас
появится
вторая
(третья,
четвёртая)
реализация
интерфейса
• Атомизируется
ответственность
объектов
• Интерфейс
не
накладывает
условий,
чем
должен
быть
передаваемый
объект:
он
может
быть
как
наследником
стандартного
объекта,
так
и
самостоятельной
реализацией
15. Правило №1: больше интерфейсов
Почему
это
полезно
при
проектировании
API:
• Выделение
интерфейсов
в
первую
очередь
необходимо
разработчику
для
наведения
порядка
в
голове
• Если
ваш
метод
принимает
в
качестве
параметра
объект
с
20
полями
и
30
методами
–
очень
рекомендуется
задуматься,
что
конкретно
необходимо
из
этих
полей
и
методов
16. Правило №1: больше интерфейсов
Результат:
• Вы
должны
получить
на
выходе
много
дробных
интерфейсов
• Ваши
сигнатуры
не
должны
требовать
от
входного
параметра
больше
7±2
свойств
или
методов
• Вы
получите
представление
о
том,
какие
свойства
ваших
объектов
важны
в
контексте
общей
архитектуры
системы,
а
какие
–
нет
• Как
следствие,
снизится
избыточность
интерфейсов
17. Правило №2: иерархия
• Ваши
объекты
должны
быть
выстроены
в
иерархию:
кто
с
кем
взаимодействует
• Объект
имеет
право
знать
только
об
объектах
соседних
уровней
Изображение
Медиатека
Файл
HDD
EXT
Директория
SSD
HFS
Программа
NTFS
Network
SMB
NFS
18. Правило №2: иерархия
Как
этого
добиться?
• Нужные
методы
и
свойства
всегда
можно
пробросить
по
цепочке
через
промежуточные
звенья
• Эта
точка
расширения
вам
рано
или
поздно
пригодится
19. Правило №2: иерархия
Почему
это
помогает
избежать
потери
обратной
совместимости:
• Снижается
общая
связанность
архитектуры,
меньше
связей
–
меньше
side-‐эффектов
• При
изменении
какого-‐либо
объекта
вы
можете
задеть
только
его
соседей
по
дереву
21. Правило №3: контексты
Пример:
• Map
=
картографический
контекст
(наблюдаемая
область
карты
+
масштаб)
• IPane
=
контекст
позиционирования
в
клиентских
координатах
• ITileContainer
=
контекст
позиционирования
в
тайловых
координатах
22. Правило №3: контексты
• Ваше
дерево
объектов
можно
будет
рассматривать
как
иерархию
контекстов
• Каждый
уровень
иерархии
должен
соответствовать
какому-‐то
уровню
абстракции
23. Правило №3: контексты
Почему
это
помогает
избежать
потери
обратной
совместимости?
• Правильно
построенное
дерево
контекстов
практически
никогда
не
изменится
при
рефакторинге:
потоки
информации
могут
появляться,
но
очень
вряд
ли
пропадут
• Правило
контекстов
позволяет
эффективно
изолировать
уровни
иерархии
друг
от
друга
24. Правило №3: контексты
Почему
это
полезно
при
проектировании
API?
• Держать
в
голове
информационную
схему
проекта
существенно
проще,
чем
полное
дерево
• Описание
объектов
в
терминах
предоставляемых
ими
контекстов
позволяет
правильно
выделять
уровни
абстракции
25. Правило №4: consistency
Любой
объект
должен
предоставлять:
• Полное
описание
своего
внутреннего
состояния
в
любой
момент
времени
• Полный
набор
событий,
позволяющий
отслеживать
все
изменения
своего
состояния
26. Правило №4: consistency
Подобные
паттерны
нарушают
принцип
consistency:
obj.name = 'что-то';
// do something
obj.setOptions('что-то');
// do something
obj.update();
В
частности,
отсюда
следует
правило:
• Избегайте
методов
update,
build,
apply
27. Правило №4: consistency
Почему
это
помогает
избежать
потери
обратной
совместимости?
• Внешний
наблюдатель
всегда
может
полностью
восстановить
состояние
и
историю
объекта
по
его
публичному
интерфейсу
• Такой
объект
всегда
можно
подменить
или
склонировать,
не
обладая
знанием
о
его
внутреннем
устройстве
28. Правило №4: consistency
Почему
это
полезно
при
проектировании
API?
• Номенклатура
методов
и
событий
ваших
объектов
становится
менее
разнообразной
и
более
консистентной
• Вам
станет
проще
выделять
интерфейсы
// object #1
this.setState = function () {
// apply changes
this.events.fire('statechange');
}
this.getState = function () {
// return state
}
// object #2
this.onObject1StateChange = function () {
var state = this.object1.getState();
// apply changes
}
34. Правило №5: события
• События
необязательны
к
исполнению
для
обоих
объектов:
вы
легко
сможете
поддерживать
такие
реализации
обоих
объектов,
которые
реагируют
только
на
часть
событий
и
отображают
только
часть
состояния
второго
объекта
• Если
у
вас
появится
третий
объект,
которому
необходимо
реагировать
на
то
же
действие
–
у
вас
не
будет
проблем
35. Правило №5: события
• Взаимодействие
на
событиях
получается
нативно
при
соблюдении
требования
consistency:
• каждый
из
объектов
знает,
когда
состояние
другого
объекта
изменилось
• каждый
из
объектов
может
полностью
выяснить
состояние
другого
• Ваша
организация
взаимодействия
между
объектами
значительно
унифицируется
• Взаимодействие
между
объектами
таким
образом
базируется
на
общих
методах
и
событиях,
а
не
частных,
т.е.
будет
содержать
гораздо
меньше
специфики
конкретных
объектов
36. Правило №6: делегирование
• Всегда
делегируйте
максимальный
объём
ответственности
объектам
нижних
уровней
• Поскольку
чаще
всего
изменяется
реализация
и
функциональность
именно
нижнего
уровня
абстракции
(верстка,
протоколы
взаимодействия,
etc),
интерфейс
к
нижнему
уровню
абстракции
должен
быть
максимально
общим
38. Правило №8: внешние источники
• В
абсолютном
большинстве
случаев
самые
большие
проблемы
с
сохранением
обратной
совместимости
возникают
вследствие
несохранения
обратной
совместимости
другими
сервисами
• Если
вы
не
контролируете
смежный
сервис
(источник
данных)
–
заведите
к
нему
версионируемую
обёртку
на
своей
стороне
40. Прежде, чем приступать
Проясните
ситуацию:
• Если
заявленный
функционал
не
работал
никогда,
вы
вольны
принять
любое
решение:
починить,
изменить,
выкинуть
• Если
что-‐то
выглядит
как
баг
–
это
ещё
не
повод
бросаться
его
чинить
41. Прежде, чем приступать
• Проверьте
тесты
на
интерфейс
объекта,
который
собираетесь
рефакторить,
и
связанных
объектов
• Если
тестов
нет
–
напишите
их
• Никогда
не
начинайте
никакой
рефакторинг
без
тестов
• Тестирование
должно
включать
в
себя
проверку
соответствия
поведения
старой
и
новой
версии
API
42. Полтора приёма рефакторинга
• Если
вы
всё
сделали
правильно
и
взаимодействие
объектов
сделано
по
схеме
"состояние
–
событие
изменения
состояния",
то,
часто,
вы
сможете
переписать
реализацию,
оставив
старые
поля
и
методы
для
обратной
совместимости
43. От релиза к релизу
• Если
вы
неправильно
назвали
сущность
–
она
будет
неправильно
называться
до
следующего
мажорного
релиза
• Если
вы
сделали
архитектурную
ошибку
–
она
будет
существовать
до
следующего
мажорного
релиза
• Запишите
себе
проблему
в
блокнотик
и
постарайтесь
не
думать
о
ней
до
следующего
мажорного
релиза