SOLID. Что значит S
Давайте поговорим о том, что такое SOLID? Наверняка вы читали книгу «Чистый Код» Роберта Мартина (как это не читали???) и вам уже давно известно, что это такое. Да и в интернете есть множество статей на эту тему. Однако же, мне, как и большинству других разработчиков, постоянно приходится работать с грязным кодом, который можно улучшить. Возникает парадокс – если все знают правила написания хорошего кода, откуда берется код плохой? В следующем блоке постов я хочу кратко рассказать, как я понимаю каждый из принципов SOLID, и, что самое главное – как мне это помогает писать более понятный и чистый код. Никаких научно-занудных объяснений. Все максимально просто, кратко и на примерах. Сегодня поговорим о том, что значит S в аббревиатуре SOLID.
S значит SRP
А SRP значит Single-Responsibility Principle. Что, в свою очередь, переводится как «принцип единой ответственности». Занудное и чисто теоретическое (но более полное) описание этого принципа можете прочитать на википедии.
SRP на примере. Аркадий в деле
Итак, у нас есть класс Employee:
public class Employee {}
Разработчик Аркадий смотрит-смотрит на этот класс и не понимает, а чего он пустой-то? Ну и решает он добавить метод writeCode()
(реализация опущена):
public void writeCode() {
// ...
}
Немного подумав и почесав затылок, Аркадий исправляет название метода на writeGoodCode()
. Потому что Аркадий — хороший разработчик!
Иннокентий негодует
Тестировщик Иннокентий, проходя мимо рабочего стола Аркадия, бросает быстрый взгляд на его монитор и замечает, что Employee
только пишет код и совсем не тестирует его.
«Ну ты, Аркаша, и растяпа», – замечает Иннокентий и просит последнего исправить ситуацию. Аркадий соглашается и добавляет соответствующий метод (реализация снова опущена):
public void test() {
// ...
}
Довольный Иннокентий с чувством выполненного долга уходит в закат. И вроде бы все хорошо, но тут…
Появляется Савелий Петрович
А Савелий Петрович, между прочим, менеджер проекта. И, как оно обычно и бывает, он коршуном налетает на бедного Аркадия и просит его «добавить фичку для клиента» в обход спринта. Внезапно Савелий Петрович замечает, что класс Employee
совсем не занимается управлением командой, и просит добавить аж ТРИ метода.
Вспотевший Аркадий добавляет в класс Employee
:
public void planSprint() {
// ...
}
public void addLittleFeature() {
// ...
}
public void manage() {
// ...
}
Савелий Петрович вдруг замечает тестировщика Иннокентия, и фокус его внимания быстро съезжает с нашего бедного Аркадия. Казалось бы, можно передохнуть, но внезапно…
Приходит Люська из аналитики
И вот в класс Employee
добавляется Люськин метод:
public void analyze() {
// ...
}
А затем…
Появилась ОНА. Аксинья Всеволодовна из отдела уборки. «А полы мыть кто будет?» – Грозно прорычала она, отобрала у Аркадия компьютер и самовольно добавила:
public Garbage cleanUp() {
// …
}
Аркадий, слегка придя в себя, робко спросил: «А зачем метод уборки что-то возвращает? Почему он не void?». Аксинья Всеволодовна будто ждала этого вопроса и гаркнула еще громче: «Да чтоб вы свой мусор все обратно получили, собаки вы сутулые!»
Что в итоге?
А в итоге класс Employee
выглядит следующим образом:
public class Employee {
public void writeGoodCode() {
// ...
}
public void test() {
// ...
}
public void planSprint() {
// ...
}
public void addLittleFeature() {
// ...
}
public void manage() {
// ...
}
public void analyze() {
// ...
}
public Garbage cleanUp() {
// ...
}
}
Похоже на SOLID? Как вам SRP?
Кто-то скажет, что Employee
– типичный fullstack, который умеет делать все на свете. Однако, задумайтесь, нужен ли вам такой fullstack?
Чем это чревато?
Допустим, уборщица Аксинья Всеволодовна прошла курсы по повышению квалификации, и теперь ей доступна тряпка более высокого уровня. Вследствие этого, мы с вами идем в класс Employee
и меняем реализацию метода cleanUp()
.
А затем наш разработчик Аркадий резко встал со своего места, воскликнул: «Да ну нах*р!» и удалился восвояси. Заместо него пришел другой разработчик, Акакий, который уже не такой опытный и пишет не такой хороший код. Поэтому мы снова идем в класс Employee
и меняем название метода writeGoodCode()
на writeCode()
.
А ведь у нас есть еще тестировщик Иннокентий, аналитик Люська и менеджер проектов Савелий Петрович – помните? Если хоть что-то деталях их работы изменится, нам снова нужно будет идти в класс Employee
.
В общем, ребята, наш супер-класс на самом деле является вонючей помойкой, куда не навалил всякой логики только лентяй. Надо ли нам поменять метод уборки помещения или же метод написания кода – мы лезем в этот класс. Поменять метод тестирования? Снова в этот класс.
Представьте, что Employee
– это вы, и ваш основной профиль – разработка. Вот вы сидите, пишете код, и тут приходит менеджер проектов и говорит: «Спланируй спринт на следующую неделю». Потом приходит аналитик и говорит: «Сделай спеку по такой-то задаче, а потом сам по ней напиши код». И под конец рабочего дня приходит уборщица: «Там унитаз засорился, а у тебя пальцы-то гибкие, иди прочисть».
И вы просто не понимаете, зачем вы должны все это делать, ведь у вас конкретный и узкий профиль. Так же и Employee
не понимает, зачем вы пихаете в него все, что не лень.
Как итог – у этого класса нет единой области ответственности, и он берет на себя слишком много. SOLID и особенно SRP тихонько негодуют в сторонке. Задачи (методы), описанные в этом классе, по-хорошему должны быть распределены на весь отдел. Давайте теперь посмотрим, как все должно быть по уму.
И тут приходит Здравый Смысл
И тут же падает в обморок от увиденного. Но уборщица Аксинья Всеволодовна выливает на него ушат холодной и грязной воды, и Здравый Смысл приходит в себя.
Он говорит: «Ребята, у вас тут не соблюдается принцип единой ответственности, также известный как SRP» и тут же начинает исправлять ситуацию и делать код более SOLID.
Метод, который отвечает за написание кода, он выносит в класс Developer
:
public class Developer {
public void writeCode() {}
}
Немного подумав, Здравый Смысл замечает, что Developer
должен еще и фиксить баги, поэтому он добавляет новый метод в тот же класс Developer
:
public class Developer {
public void writeCode() {}
public void fixBug() {}
}
«Отлично!», – восклицает Савелий Петрович, – «Теперь вся логика по разработке и багфиксу сосредоточена в одном месте!»
Здравый Смысл согласно кивает и создает класс ProjectManager
:
public class ProjectManager {
public void planSprint() {}
public void addLittleFeature() {}
public void manage() {}
}
Немного погодя, он удаляет метод addLittleFeature()
, и Савелий Петрович в слезах удаляется. Но это уже тема для отдельного разговора.
Здравый Смысл, тем временем, не отдыхает и создает новые классы:
public class Tester {
public void test() {}
}
public class Analyst {
public void analyze() {}
}
public class Cleaner {
public void cleanUp() {}
}
Что стало лучше?
Здравый Смысл сажает всех вокруг себя и говорит следующие золотые слова:
– Теперь у каждого из вас есть своя зона ответственности. Акакий, если тебе нужно будет добавить новый метод или изменить существующий, ты пойдешь в класс Developer
. И тебя больше не будут просить прочистить унитаз! Иннокентий, ты у нас теперь только тестируешь, и твой класс называется Tester
. Люська сидит в Analyst
, поэтому для изменений в ее работе вам следует отправиться именно туда. Савелий Петрович, ваша работа теперь заключена в классе ProjectManager
, и никаких больше маленьких фич! Ну а вы, уважаемая Аксинья Всеволодовна, теперь сосредоточены в Cleaner
, и не смейте больше свои обязанности скидывать на бедного Акакия! Давайте соблюдать SRP! У каждого из вас теперь своя зона ответственности!
Все встают и отправляются заниматься своей работой.
И это далеко не все
Представьте, что Здравый Смысл не приходил, и наш класс Employee
по-прежнему выглядит ужасающе.
И вдруг случается так, что уборщица в офисе этажом выше приболела, и они попросили на время нашу Аксинью Всеволодовну. А она, на самом-то деле, добрейшей души бабка, и не может отказать людям в беде.
А так как она часть Epmloyee
, то она заодно хватает с собой всех остальных: Акакия, Иннокентия, Люську и Савелия Петровича. И вот они приходят в офис этажом выше. Аксинья Всеволодовна приступает к работе, а остальные стоят вдоль стены и молча смотрят.
А теперь давайте перейдем к коду. Допустим, есть класс Office
, и ему необходим только один метод: cleanUp()
.
Выглядит он как-то так:
public class Office {
private final Employee employee;
public Office(Employee employee) {
this.employee = employee;
}
public void cleanUp() {
employee.cleanUp();
}
}
Возникает вопрос – если классу нужен лишь один метод cleanUp()
, зачем ему объект, который, кроме этого метода, содержит еще кучу других, никак с уборкой не связанных? Иными словами, руководство офиса недоуменно смотрит на Акакия, Иннокентия, Люську и Савелия Петровича, которые как идиоты стоят у стены и смотрят, как Аксинья Всеволодовна убирается.
Вот как бы выглядел класс Office после визита Здравого Смысла:
public class Office {
private final Cleaner cleaner;
public Office(Cleaner cleaner) {
this.cleaner = cleaner;
}
public void cleanUp() {
cleaner.cleanUp();
}
}
Просили уборщицу – пришла уборщица. Одна. Без кучи лишних зависимостей. Все ясно и понятно.
Вывод
Следуй здравому смыслу. Не нарушай SRP.
Понравилось? Подписывайтесь на меня в соцсетях!