Принцип SRP vs Принцип KISS. Как найти баланс?
Всем привет, сегодня я хотел бы поделиться опытом, как писать код так, чтобы система была с одной стороны гибкой, а с другой – простой и компактной.
Довольно часто бывает так, что разработчик открывает чужой код и видит одну из двух ситуаций:
- Большие классы, которые решают несколько задач разного типа
- Маленькие классы, которые выполняют одну ответственность, но тесно связанны друг с другом
Знакомо? И в том и в другом случае неудобно развивать и поддерживать код, поскольку в первом случае нарушен принцип единственной ответственности, который гласит, что каждый класс должен иметь одну причину для изменения, а во втором – KISS, который гласит, что более простые системы лучше поддерживаются и реже дают сбои.
На самом деле, когда нужно реализовать комплексную систему, необходимо соблюдать баланс между простой и гибкостью. Это значит, что иногда нужно нарушать принцип единственной ответственности ради простоты и компактности системы, а в остальных случаях разбивать код по SRP делая его более гибким и переиспользуемым.
В первую очередь, когда я разрабатываю или рефакторю систему, я думаю о том, чтобы она была простая и компактная, а во вторую – закладываю точки гибкости, где она может расширяться. Хочу подчеркнуть важный аспект: не нужно делать сразу универсальную систему, которую можно масштабировать в любом направлении, потому что чем гибче система, тем сложнее она будет выглядеть. Точки гибкости добавляются по ходу эволюции фичи.
Таким образом, я сделал для себя некоторый уровень допущения того, когда нужно 100% разбивать класс по SRP, а когда это сделать желательно.
- Класс 100% нужно разбить по SRP, если он выполняет ответственности разного рода. Например, система врагов, которая помимо спауна занимается катсценами или персонаж на сцене, который помимо перемещения занимается подключением к базе данных. Примеры утрированные, но я думаю, суть вы уловили.
- Класс желательно разбить по SRP, если он выполняет группу ответственностей одного рода. Например, менеджер квестов, который занимается созданием, хранением квестов, а также их генерацией и выдачей награды. Несмотря на то, что менеджер имеет много полей, методов и зависимостей, этот класс выполняет глобальную ответственность управления квестами. Если этот менеджер будет в процессе разработки и дальше разростаться, то его уже можно будет подразбить для упрощения работы с этой фичей.
В завершение скажу, что не всегда легко искать золотую середину, нужна практика и самоанализ. К тому же рекомендую изучить шаблоны GRASP, Low Coupling & High Cohesion, которые тоже будут помогать находить этот баланс.