SOLID in React

Solid principles

Solid principles

Хочется вспомнить SOLID принципы и рассмотреть, как можно их применять в разработке интерфейсов на примере React компонентов.

S: Single Responsibility Principle (Принцип единственной ответственности). Означает, что каждый класс/функция/компонент должны выполнять только одну конкретную задачу.

На примере React компонента: компонент, который отрисовывает пользовательский интерфейс, не должен содержать в себе логику авторизации этого пользователя.

O: Open-Closed Principle (Принцип открытости-закрытости). Означает, что класс/функция/компонент должны быть открыты для расширения, но закрыты для модификации. Чтобы их можно было расширять новым функционалом, не изменяя при этом исходный код.

На примере React компонента:

Open-Closed Principle

Open-Closed Principle

На примере выше мы имеем абстрактную базовую кнопку, которую мы не можем изменять, но можем расширять, за счет передачи таких значений как className, onClick и children

Еще один способ реализовать данный принцип в React — использовать HOC (читается не как нос, а как хок) (Higher-Order Component) — это функция, которая принимает компонент и возвращает новый компонент с добавленным поведением или функциональностью. Это отличный способ повторно использовать логику в разных компонентах.

Пример использования HOC с компонентом кнопки

Пример использования HOC с компонентом кнопки

L: LSP (Принцип подстановки Барбары Лисков). Это означает, что объекты базовых классов должны быть заменяемы объектами производных классов без изменения ожидаемого поведения программы. То есть производные классов должны мочь использоваться в тех местах, где используется базовый класс без каких-либо изменений

На примере React компонента:

LSP

LSP

В базовом классе Payment мы определяем метод processPayment, который будет обрабатывать платеж. Этот метод должен быть реализован в подклассах.

Оба подкласса (CreditCardPayment и PayPalPayment) реализуют метод processPayment, каждый по-своему. Но оба класса сохраняют контракт, заданный базовым классом, и могут быть использованы взаимозаменяемо.

Функция processOrder принимает объект paymentMethod, который является экземпляром любого класса, наследующего Payment, и вызывает метод processPayment. Благодаря принципу подстановки Лисков, мы можем передавать как CreditCardPayment, так и PayPalPayment в функцию processOrder, и она будет корректно обрабатывать любой тип платежа.

Тоже самое работает с примером с кнопками, каждый вид кнопки, который расширил базовую, может использоваться вместо базовой без каких-либо изменений, соответственно выполняется прицип LSP.

I: ISP (Принцип разделения интерфейса). Означает что классы/функции/компоненты не должны зависеть от интерфейсов, в которых они не нуждаются.

На примере React компонента: самое простое объяснение нарушения данного принципа — передавать в компонент огромный объект, а использовать только одно его поле.

Нарушение принципа ISP

Нарушение принципа ISP

В примере выше, стоит в компонент UserName передавать только поле имени, а не весь объект

Соответствие принципу ISP

Соответствие принципу ISP

D: DIP (Принцип инверсии зависимостей). Означает что высокоуровневые модули не должны зависеть от низкоуровневых модулей

На примере React компонента: рассмотрим такой компонент

Пример нарушения DIP

Пример нарушения DIP

Мы имеем простой компонент с кнопкой, по нажатию на которую происходит логирование. В данном примере высокоуровневым компонентом у нас является кнопка, а низкоуровневым — функция логгер (ConsoleLogger) ее нажатия. Как мы видим кнопка напрямую зависит от реализации данного логгера, а согласно принципу, она должна зависеть от абстракции. Проблема в том, что если придется использовать другой способ логирования, то придуется изменять код компонента

Посмотрим на компонент, соответствующий принципу DIP

Компонент, который соответствует принципу DIP

Компонент, который соответствует принципу DIP

Мы определили интерфейс функции логгера, создали несколько конкретных реализаций логгера, которые соответствуют определенному интерфейсу. Компонент UserService теперь принимает логгер через пропс, что позволяет легко менять реализацию логгера, не изменяю при этом код компонента. Благодаря чему, мы можем использовать UserService с любым логгером, который реализует интерфейс ILogger

Пример использования

Пример использования

Заключение

Таким образом, несмотря на то, что принципы SOLID были изначально разработаны и применялись для ООП разработки, оказывается, что их можно вполне успешно применять в функциональном программировании при разработке интерфейсов — круто

Если понравилось — подписывайтесь на канал в тг, буду писать интересное там

© Habrahabr.ru