Для сотрудничестваarrow
Telegram

SOLID

render queue

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

Single Responsibility
(Принцип единственной ответственности)

1interface ButtonProps {
2  onClick: () => void;
3  text: string;
4}
5
6const Button: React.FC<ButtonProps> = ({ onClick, text }) => {
7  return <button onClick={onClick}>{text}</button>;
8};9
9
10export default Button;

Компонент <Button> в React отражает принцип единственной ответственности (Single Responsibility Principle), поскольку компонент<Button> выполняет только одну задачу - отображение кнопки и обработку кликов на нее. Это позволяет компоненту быть легко понятным, поддерживаемым и переиспользуемым, так как он не содержит избыточной логики или функциональности, не относящейся к его основной ответственности.

Open-Closed
(Принцип открытости-закрытости)

1interface ButtonProps {
2  onClick: () => void;
3  text: string;
4  variant?: 'primary' | 'secondary'; 
5  // Расширение за счет добавления нового свойства
6}
7
8const Button: React.FC<ButtonProps> = ({
9  onClick,
10  text,
11  variant = 'primary',
12}) => {
13  const buttonStyle =
14    variant === 'primary'
15      ? { backgroundColor: 'blue', color: 'white' }
16      : { backgroundColor: 'gray', color: 'black' };
17
18  return (
19    <button style={buttonStyle} onClick={onClick}>
20      {text}
21    </button>
22  );
23};
24
25export default Button;
1interface ButtonProps {
2  onClick: () => void;
3  text: string;
4  variant?: 'primary' | 'secondary'; 
5  size?: 'sm' | 'base' | 'lg'; // Расширение
6}

Компонент <Button>, принцип Open/Closed означает, что мы можем расширять функциональность компонента (например, добавлять новые типы вариантов кнопок) без изменения исходного кода самого компонента. Это достигается за счет использования дополнительного свойства variant, которое позволяет выбирать стиль кнопки на основе переданного значения. Таким образом, компонент остается закрытым для модификации, но открытым для расширения новыми вариантами кнопок.

Liskov Substitution
(Принцип подстановки Барбары Лисков)

1interface ButtonProps {
2  onClick: () => void;
3  text: string | React.ReactNode;
4}
5
6const BaseButton: React.FC<ButtonProps> = ({ onClick, text }) => {
7  return <button onClick={onClick}>{text}</button>;
8};
9
10export default BaseButton;
1import BaseButton from './BaseButton';
2
3interface IconButtonProps {
4  onClick: () => void;
5  icon: string;
6}
7
8const IconButton: React.FC<IconButtonProps> = ({ onClick, icon }) => {
9  return <BaseButton  onClick={onClick} text={<i className={`icon-${icon}`}/>}/>;
10};
11
12export default IconButton;

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

Interface Segregation
(Принцип разделения интерфейсов)

1interface InputFieldProps {
2  label: string;
3  type: string;
4  name: string;
5  onChange: (value: string) => void;
6}
7
8const InputField: React.FC<InputFieldProps> = ({ 
9  label, 
10  type, 
11  name, 
12  onChange 
13}) => {
14  return (
15    <div>
16      <label>{label}</label>
17      <input 
18        type={type} 
19        name={name} 
20        onChange={(e) => onChange(e.target.value)} 
21      />
22    </div>
23  );
24};
1interface CheckboxProps {
2  label: string;
3  checked: boolean;
4  onChange: (checked: boolean) => void;
5}
6
7const Checkbox: React.FC<CheckboxProps> = ({ 
8  label, 
9  checked, 
10  onChange 
11}) => {
12  return (
13    <div>
14      <label>{label}</label>
15      <input 
16        type="checkbox" 
17        checked={checked} 
18        onChange={(e) => onChange(e.target.checked)} 
19      />
20    </div>
21  );
22};
1import React, { useState } from 'react';
2
3const Form: React.FC = () => {
4  const [formData, setFormData] = useState<any>({});
5
6  const handleInputChange = (name: string, value: string) => {
7    setFormData({ ...formData, [name]: value });
8  };
9
10  const handleCheckboxChange = (name: string, checked: boolean) => {
11    setFormData({ ...formData, [name]: checked });
12  };
13
14  return (
15    <form>
16      <InputField 
17        label="Username" 
18        type="text" 
19        name="username" 
20        onChange={(value) => handleInputChange('username', value)} 
21      />
22      <Checkbox 
23        label="Remember me" 
24        checked={formData.rememberMe || false} 
25        onChange={(checked) => handleCheckboxChange('rememberMe', checked)} 
26      />
27    </form>
28  );
29};
30
31export default Form;

Принцип разделения интерфейса (Interface Segregation Principle) гласит, что клиенты не должны зависеть от интерфейсов, которые они не используют. В примере компонента формы с разделением интерфейса на более мелкие части, каждый компонент (<InputField> и <Checkbox>) предоставляет отдельный интерфейс для конкретной функциональности (ввод текста и выбор чекбокса). Это позволяет клиентам (в данном случае компоненту <Form>) использовать только те части интерфейса, которые им нужны, минимизируя зависимости от ненужных функций. Таким образом, принцип Interface Segregation способствует созданию более гибких и модульных компонентов.

Dependency Inversion
(Принцип инверсии зависимостей)

1interface DataService {
2  fetchData: () => Promise<any>;
3}
4
5interface DisplayDataProps {
6  data: any;
7}
8
9const DisplayData: React.FC<DisplayDataProps> = ({ data }) => {
10  return <div>{data}</div>;
11};
12
13const DataComponent: React.FC<{ service: DataService }> = ({ 
14  service 
15}) => {
16  const [data, setData] = React.useState<any>(null);
17
18  React.useEffect(() => {
19    service.fetchData().then((result) => setData(result));
20  }, [service]);
21
22  return <DisplayData data={data} />;
23};
24
25export default DataComponent;

Принцип инверсии зависимостей (Dependency Inversion Principle) гласит, что модули должны зависеть от абстракций, а не от конкретных реализаций. В примере компонента<DataComponent>, он зависит от абстракции DataService, которая определяет метод fetchData. При этом конкретная реализацияDataService может быть передана в <DataComponent> как параметр.

Это позволяет легко заменять конкретные реализации DataService без изменения самого<DataComponent>, так как <DataComponent> зависит от абстракции DataService, а не от конкретной реализации. Таким образом, принцип инверсии зависимостей обеспечивает более гибкую и расширяемую архитектуру приложения.

Импортирование сервиса напрямую в компонент противоречит принципу инверсии зависимостей (Dependency Inversion Principle), так как это создает прямую зависимость компонента от конкретной реализации сервиса. Вместо этого, принцип рекомендует передавать сервис в компонент через параметры или через внедрение зависимостей (dependency injection), чтобы компонент зависел от абстракции (интерфейса), а не от конкретной реализации. Это делает код более гибким, позволяет легко заменять реализации сервисов и упрощает тестирование компонентов.