Skip to content

Commit

Permalink
refs #119 init SOLID page
Browse files Browse the repository at this point in the history
  • Loading branch information
tabuna committed Apr 14, 2024
1 parent 1f64432 commit 117fbdf
Show file tree
Hide file tree
Showing 8 changed files with 500 additions and 1 deletion.
34 changes: 34 additions & 0 deletions resources/views/library/solid.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@extends('layout')
@section('title', 'SOLID принципы')
@section('description', 'Мы поочередно познакомимся с каждым принципом, чтобы понять, как SOLID может помочь вам стать лучшим разработчиком.')
@section('content')

<x-header align="align-items-end">
<x-slot name="sup">Объектно-ориентированные</x-slot>
<x-slot name="title">SOLID принципы</x-slot>
<x-slot name="description">
Мы поочередно познакомимся с каждым принципом, чтобы понять, как SOLID может помочь вам стать лучшим разработчиком.
</x-slot>
<x-slot name="content">
<figure class="d-none d-md-block">
<x-icon path="l.dots" class="text-primary opacity-2 d-block mx-auto" height="300" width="300"/>
</figure>
</x-slot>
</x-header>

@php
$sections = collect([
'basics',
'srp',
'ocp',
'lsp',
'lsp',
'dip',
])
->map(fn ($file) => \Illuminate\Support\Str::of($file)->start('solid/'))
->map(fn ($file) => new \App\Library($file));
@endphp

@include('particles.library-section', ['sections' => $sections])

@endsection
2 changes: 1 addition & 1 deletion routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
Route::view('/library/security', 'library.security')->name('library.security');
Route::view('/library/how-to-ask', 'library.how-to-ask')->name('library.how-to-ask');
Route::view('/library/collection', 'library.collection')->name('library.collection');

Route::view('/library/solid', 'library.solid')->name('library.solid');
/*
|--------------------------------------------------------------------------
| Open Quiz
Expand Down
15 changes: 15 additions & 0 deletions storage/library/solid/basics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: "Введение"
description: "<strong>SOLID</strong> - это акроним для первых пяти принципов объектно-ориентированного проектирования от Роберта Мартина."
---

*SOLID* - это основа объектно-ориентированного проектирования (ООП), которая помогает создавать гибкие, расширяемые и
поддерживаемые программы. Когда ты понимаешь эти принципы, ты можешь писать код, который легко изменять и дополнять.

Эти принципы не только полезны для написания качественного кода, но и часто встречаются на собеседованиях на должности
разработчика. Рекрутеры и работодатели ценят знание SOLID, так как это указывает на твою способность создавать
высококачественное программное обеспечение.

Понимание SOLID помогает избежать типичных проблем, таких как сложная поддержка кода, неожиданные ошибки и сложности в
расширении функциональности. Поэтому освоение этих принципов не только делает тебя лучшим разработчиком, но и помогает
тебе быть востребованным на рынке труда.
71 changes: 71 additions & 0 deletions storage/library/solid/dip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: "Принцип инверсии зависимостей"
description: "Любые более высокие (дочерние) классы всегда должны зависеть от абстракций, а не от деталей."
---

Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) гласит, что модули высокого уровня не должны
зависеть от модулей низкого уровня, а оба типа модулей должны зависеть от абстракций. Также он утверждает, что
абстракции не должны зависеть от деталей, а детали должны зависеть от абстракций.

Давай разберемся, что это означает на примере:

```php
<?php
// Нарушение принципа инверсии зависимостей
class Mailer
{

}

class SendWelcomeMessage
{
private $mailer;

public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
}
```

Здесь класс SendWelcomeMessage зависит от конкретной реализации Mailer, что нарушает принцип инверсии зависимостей. Если
мы захотим изменить способ отправки сообщений, нам придется изменять и класс SendWelcomeMessage.

Для исправления этой проблемы мы можем использовать абстракцию в виде интерфейса Mailer:

```php
interface Mailer
{
public function send();
}

class SmtpMailer implements Mailer
{
public function send()
{
// Реализация отправки через SMTP
}
}

class SendGridMailer implements Mailer
{
public function send()
{
// Реализация отправки через SendGrid
}
}

class SendWelcomeMessage
{
private $mailer;

public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
}
```

Теперь класс SendWelcomeMessage зависит от абстракции Mailer, а не от конкретной реализации. Мы можем легко изменить
способ отправки сообщений, просто передавая нужную реализацию интерфейса Mailer в конструктор SendWelcomeMessage. Это
соответствует принципу инверсии зависимостей.
119 changes: 119 additions & 0 deletions storage/library/solid/isp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: "Принцип разделения интерфейса"
description: "Интерфейсов должно быть много."
---

Принцип разделения интерфейса (Interface Segregation Principle, ISP) предписывает, что клиенты не должны зависеть от
методов, которые они не используют. Вместо этого интерфейсы должны быть разделены на более мелкие, специализированные
интерфейсы, чтобы клиенты могли реализовывать только те методы, которые им нужны.

Давай разберем, как это работает на примере:

```php
// Нарушение принципа разделения интерфейса
interface Workable
{
public function canCode();
public function code();
public function test();
}

class Programmer implements Workable
{
public function canCode()
{
return true;
}

public function code()
{
return 'coding';
}

public function test()
{
return 'testing in localhost';
}
}

class Tester implements Workable
{
public function canCode()
{
return false;
}

public function code()
{
throw new Exception('Opps! I can not code');
}

public function test()
{
return 'testing in test server';
}
}

class ProjectManagement
{
public function processCode(Workable $member)
{
if ($member->canCode()) {
$member->code();
}
}
}
```

В этом примере интерфейс Workable содержит методы canCode(), code() и test(). Проблема в том, что не все классы,
реализующие этот интерфейс, могут выполнять все эти действия. Например, класс Tester не может кодировать, но он должен
реализовать метод code(), потому что интерфейс Workable требует это.

Чтобы исправить это, мы можем разделить интерфейс на более мелкие и специализированные интерфейсы:

```php
// Улучшенный вариант
interface Codeable
{
public function code();
}

interface Testable
{
public function test();
}

class Programmer implements Codeable, Testable
{
public function code()
{
return 'coding';
}

public function test()
{
return 'testing in localhost';
}
}

class Tester implements Testable
{
public function test()
{
return 'testing in test server';
}
}

class ProjectManagement
{
public function processCode(Codeable $member)
{
$member->code();
}
}
```

Теперь интерфейсы Codeable и Testable более специализированы. Класс Programmer реализует оба интерфейса, потому что он
может кодировать и тестировать. Класс Tester реализует только интерфейс Testable, потому что он может только
тестировать. Таким образом, классы могут реализовывать только те методы, которые им нужны, что соответствует принципу
разделения интерфейса.
89 changes: 89 additions & 0 deletions storage/library/solid/lsp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: "Принцип подстановки Барбары Лисков"
description: "Дочерние классы должны работать так, что бы ими можно было заменить родительские."
---

Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) утверждает, что поведение подклассов должно быть
совместимо с поведением их суперклассов. Другими словами, объекты подтипов должны быть заменяемыми экземплярами своих
супертипов без изменения ожидаемого поведения программы. Давай разберемся с примером, чтобы лучше понять этот принцип:

```php
<?php

// Нарушение принципа подстановки Барбары Лисков
// Проблема квадрата и прямоугольника
class Rectangle
{
protected $width;
protected $height;

public function setHeight($height)
{
$this->height = $height;
}

public function getHeight()
{
return $this->height;
}

public function setWidth($width)
{
$this->width = $width;
}

public function getWidth()
{
return $this->width;
}

public function area()
{
return $this->height * $this->width;
}
}

class Square extends Rectangle
{
public function setHeight($value)
{
$this->width = $value;
$this->height = $value;
}

public function setWidth($value)
{
$this->width = $value;
$this->height = $value;
}
}

class RectangleTest
{
private $rectangle;

public function __construct(Rectangle $rectangle)
{
$this->rectangle = $rectangle;
}

public function testArea()
{
$this->rectangle->setHeight(2);
$this->rectangle->setWidth(3);
// Ожидаем, что площадь прямоугольника будет 6
}
}
```

В данном примере класс Square наследуется от Rectangle, что кажется логичным, так как квадрат является частным случаем
прямоугольника. Однако, нарушается принцип подстановки Барбары Лисков из-за того, что Square переопределяет методы
setHeight() и setWidth() так, чтобы они всегда делали высоту равной ширине.

Что делает этот пример нарушением LSP? Дело в том, что ожидается, что при вызове setHeight() и setWidth() объекта
Rectangle сначала будет изменяться одно измерение, а потом другое. Однако в случае Square эти методы нарушают это
ожидание, что может привести к непредсказуемому поведению в программах, которые используют Rectangle или его подтипы.

Как исправить это? Один из способов - пересмотреть архитектуру классов так, чтобы Square не наследовался от Rectangle,
так как это нарушает LSP. Вместо этого, можно использовать композицию или выделить общий интерфейс для обоих классов и
разработать их независимо.
Loading

0 comments on commit 117fbdf

Please sign in to comment.