S.O.L.I.D. Что такое SOLID и кому это надо? Часть 1.

SOLID принципы – что это? Если вы работаете программистом и любите свою работу – скорее всего вы хотя бы раз слышали такое слово как SOLID. Но только слышать недостаточно, неплохо бы понять как это все использовать. Для того чтобы разобраться зачем это нужно обычному программисту — предлагаю для начала понять вообще что такое этот SOLID. Возможно вы используете его принципы сами того не понимая. Возможно понимаете, но не используете. А возможно не понимаете и не используете.

SOLID — это аббревиатура (на самом деле нет), нашумевшая в мире программистов поклоняющихся ООП. Каждая буква — это начало определенного принципа, которому желательно следовать чтобы считать себя первоклассным программистом. Если вы скажете на собеседовании что вы крутой разработчик, но не расскажете про SOLID — вам тут же укажут на дверь, заставят вас краснеть и чувствовать себя неловко. А если вы просите еще зарплату синьйора — то такого позора даже не чувствовала Серсея когда ее гнали голой по улицам Вестероса. В противном случае, если вопрос этот был проигнорирован — вам самим необходимо бежать с этого собеседования и писать отзывы везде где только можно что компания нанимает безграмотных  разработчиков.

На самом деле, из названия некоторых принципов понятно в целом о чем речь. Из названия других – не совсем понятно.  Из третьих – вообще не понятно ничего. Но так как я начал писать эту статью – то придется рассмотреть каждый из них, каким-бы понятным-непонятным он не оказался. Точка.

Single responsibility. Первый принцип SOLID.

Первая буква SOLID. Это тот пункт, в котором должно быть все понятно из названия. Если не совсем ясно – то вот объяснение. Этот принцип означает, что любой класс должен отвечать за одну конкретную задачу и решать только ее. Если вы видите, что класс не только преобразует текст в верхний регистр, но и запускает спутник в космос, кормит кота вашего соседа и заказывает вино вам на дом – то здесь что-то не то… Что-то надо менять. Я бы на вашем месте создал ещё классов. Или сменил бы работу. И это определенно понятно. Это и есть Single responsibility или же Принцип единственной ответственности.

Конечно теория – это хорошо, но без примеров статья будет ни о чем. Давайте, к примеру, рассмотрим такой класс, который распространяет сплетни. У нас есть сама сплетня $message, мы можем ее получить с помощью метода getGossip, так же у каждой сплетни есть автор,который их распускает. Для того чтобы узнать автора сплетни воспользуемся методом getGossipAuthor. Так же мы хотим ее рассказать нашим самым близким друзьям по сплетням – это Боб и Элис. Они это оценят! Для этого мы используем метод sayGossipOnlyToBobAndAlice. В случае, если мы хотим сохранить эту сплетню, чтобы использовать ее через пару лет – то метод saveGossip нам в этом поможет. Давайте теперь посмотрим на наш класс.

class Gossip
{
	private $message;

	private $author;
	
	public function getGossip()
	{
		return $this->message;
	}

	public function getGossipAuthor()
	{
		return $this->author;
	}

	public function sayGossipOnlyToBobAndAlice()
	{
		// ...
	}

	public function saveGossip()
	{
		// ...
	}

}

Вроде бы отличный класс, работает со сплетнями да и все, зачем придираться к нему? И вроде все достаточно хорошо, но все равно какая-то ерунда. Смотрите на этот класс до тех пор, пока вас не начнет что-то  бесить и руки не попросят что-то изменить.

На самом деле этот класс позволяет себе слишком много. Я бы ограничил его действия на вашем месте. Что, если у меня появится еще один сплетник-друг, или их будет целая группа?  Что если я захочу шантажировать сплетней и рассказать ВСЕМ, не только моим дорогим друзьям? А как это лучше сделать: email, смс, послание с самолета, голубь, шепотом на ушко?  Явно что sayGossipOnlyToBobAndAlice здесь лишний. Это лучше вынести в отдельный сервис, который сам решит как и куда ему передавать сплетню. Второй метод – это saveGossip. Мы можем хранить по разному данные: MySQL, txt-файл, дома в записной книге, в своей голове или капсуле времени. Для этого подойдет отдельный класс, который будет работать непосредственно с хранением данных.

В итоге, из данного класса я бы оставил следующее:

class Gossip
{
	private $message;

	private $author;
	
	public function getGossip()
	{
		return $this->message;
	}

	public function getGossipAuthor()
	{
		return $this->author;
	}
}

Open-closed.

По этому принципу может быть не совсем понятно что он значит. В двух словах – класс должен быть открытым для расширения, но закрытым для модификации. Вроде бы так просто. То есть, если не хватает функционала класса, а очень и очень хочется этого – должна быть возможность привнести его каким-то образом, при этом не изменяя текущий класс.

Давайте рассмотрим класс, который будет говорить “Привет”. Но делать он это будет по-разному в зависимости от текущего настроения. Вы же тоже так делаете, почему бы и классу этого не позволить.

class SayHello
{
	private $mood;

	public function sayHello()
	{
		if ($this->mood === ‘awsome’) {
			return ‘Приветики :) Чмафффкии’;
		} elseif ($this->mood === ‘normal’) {
			return ‘Ну Здравствуй.’;
		} elseif($this->mood === ‘bad’) {
			return ‘Че? ЧеНннада?’;
		}

		return ‘Пока.’;
	}
}

Вроде все понятно. Все как в жизни. Но что нам делать, если у нас появилось новое настроение, которого нет в списке? Или через пол года у нас появится какое-то настроение о котором мы даже не подозревали. Придется добавлять еще одно условие в этот метод. Но что делать, если нам запретили менять этот класс и трогать его руками каким-бы то ни было образом? Вообще! Даже нет варианта кому-то заплатить чтобы вам разрешили в нем что-то напечатать, и это даже не класс друга вашего кума/брата/дяди через которых можно как-то по-знакомству устранить эту нелепицу.

Если нельзя использовать чужой класс – можно всегда написать свой! И никто об этом не узнает. Наверное. Шучу, узнают все.

Давайте создадим класс SayAwsomeHello который будет наследовать наш такой гордый и недоступный класс SayHello.

class SayHello
{
	public function sayHello()
	{
		return ‘Привет.’;
	}
}

class SayAwsomeHello extends SayHello
{
	public function sayHello()
	{
		return ‘Приветики :) Чмафффкии’;
	}
}

По аналогии поступим с другими настроениями:

class SayNormalHello extends SayHello
{
	public function sayHello()
	{
		return ‘Ну Здравствуй.’;
	}
}

class SayBadHello extends SayHello
{
	public function sayHello()
	{
		return ‘Че? ЧеНннада?’;
	}
}

Таким образом, когда у нас будет новое настроение – мы с легкостью сможем добавить его в наш код.

Продолжение читайте во второй части.

Leave a Reply