12.1.1 — первая буква SOLID12.1.1 — the first letter of SOLID

Single Responsibility Principle (SRP)

У модуля должна быть только одна причина для изменения. Не «одна функция должна делать одну вещь» (это скорее про размер функции) — SRP формулирует границу ответственности через кто инициирует изменение, а не через размер кода.A module should have only one reason to change. It isn't "a function should do one thing" (that's more about function size) — SRP defines the boundary of responsibility by who would request a change, not by how much code there is.

частый вопрос на собеседованииcommon interview question SOLID · 1/5

Что этоWhat it is

Формулировка Роберта Мартина: «класс должен иметь только одну причину для изменения» (a class should have only one reason to change). Позднее он уточнил её через понятие актора — группы людей/подсистем, ради которых модуль меняется. Если один и тот же класс могут заставить измениться два разных актора по двум не связанным друг с другом причинам, он нарушает SRP, даже если методов в нём немного.Robert Martin's formulation: "a class should have only one reason to change." He later refined it through the concept of an actor — a group of people/subsystems for whose sake a module changes. If two different actors could each force the same class to change, for two unrelated reasons, it violates SRP — even if the class has very few methods.

Классический пример — класс Employee с методами calculatePay() (нужен бухгалтерии), save() (нужен DBA) и reportHours() (нужен HR). Три разных актора могут независимо потребовать изменить один и тот же класс — а значит, изменение по запросу бухгалтерии рискует случайно сломать логику сохранения в БД, хотя они логически никак не связаны.The classic example: an Employee class with methods calculatePay() (needed by accounting), save() (needed by the DBA), and reportHours() (needed by HR). Three different actors can each independently demand a change to the same class — meaning a change requested by accounting risks accidentally breaking the database-save logic, even though the two are logically unrelated.

До и послеBefore and after

нарушение SRP — три актора, один классSRP violation — three actors, one class
struct Employee {
    double calculatePay() const;    // нужен бухгалтерии// needed by accounting
    void save(Database& db) const;  // нужен DBA// needed by the DBA
    std::string reportHours() const;   // нужен HR// needed by HR
};
три класса — по одному на актораthree classes — one per actor
struct Employee { /* только данные и инварианты сотрудника *//* only employee data and invariants */ };

struct PayCalculator {
    double calculate(const Employee&) const;
};
struct EmployeeRepository {
    void save(const Employee&, Database&) const;
};
struct HoursReporter {
    std::string report(const Employee&) const;
};

Теперь изменение формулы расчёта зарплаты трогает только PayCalculator и не может случайно задеть код сохранения в БД или формирования отчёта — каждый класс перекомпилируется и тестируется независимо от причин, требующих правки двух других.Now a change to the pay-calculation formula only touches PayCalculator and can't accidentally affect the database-save code or the report-generation code — each class recompiles and gets tested independently of the reasons that would require editing the other two.

ЗачемWhy

01

Изоляция причин изменений снижает риск регрессийIsolating reasons for change reduces regression risk

Если у класса одна причина для изменения, правка по этой причине не может случайно сломать несвязанную функциональность — потому что несвязанной функциональности в этом классе просто нет.If a class has exactly one reason to change, a change made for that reason cannot accidentally break unrelated functionality — because there is no unrelated functionality in that class to break.

02

Меньше merge-конфликтов между командамиFewer merge conflicts between teams

Когда бухгалтерия и HR правят один и тот же файл ради своих не связанных задач, растёт вероятность конфликтов слияния и code review, требующего понимания сразу двух доменов. Разделение по актору естественно разносит правки по разным файлам.When accounting and HR both edit the same file for their unrelated tasks, the odds of merge conflicts rise, along with the need for a code review that understands two domains at once. Splitting by actor naturally routes edits to different files.

03

Высокая связность (High Cohesion) как побочный эффектHigh cohesion as a side effect

Класс с одной причиной для изменения почти всегда обладает высокой внутренней связностью — все его поля и методы используются вместе, ради одной цели, что упрощает понимание и переиспользование.A class with a single reason to change almost always has high internal cohesion — all its fields and methods are used together, toward one goal, which makes it easier to understand and reuse.

ГраблиGotchas

SRP — не «один метод на класс»SRP is not "one method per class"

Частая переинтерпретация: дробить класс на как можно более мелкие части. Класс с 10 методами может прекрасно соблюдать SRP, если все 10 методов служат одному актору и одной ответственности — и наоборот, класс с одним методом может уже нарушать принцип, если внутри него смешаны решения для разных акторов.A common misreading: split a class into the smallest possible pieces. A class with 10 methods can perfectly satisfy SRP if all 10 serve one actor and one responsibility — conversely, a class with a single method can already violate the principle if that method's internals mix concerns belonging to different actors.

Чрезмерное дробление создаёт «класс-анемию» и хрупкую координациюOver-splitting creates "anemic classes" and fragile coordination

Разбиение ради самого разбиения превращает простую операцию в цепочку из пяти крошечных классов, которые нужно правильно оркестрировать в вызывающем коде — сложность не исчезает, а переезжает туда, где её сложнее увидеть и протестировать.Splitting for its own sake turns a simple operation into a chain of five tiny classes that the calling code now has to orchestrate correctly — the complexity doesn't disappear, it just relocates somewhere harder to see and test.

Актор — не то же самое, что «функциональная область»An actor is not the same thing as a "functional area"

Два метода, формально относящиеся к разным «областям» (валидация и логирование), но всегда меняющиеся вместе по запросу одного и того же актора (например, requirements продукта), могут законно жить в одном классе — критерий SRP не топологический, а организационный.Two methods that formally belong to different "areas" (validation and logging), but that always change together at the request of the same actor (say, product requirements), can legitimately live in the same class — SRP's criterion is organizational, not topological.