← Назад к вопросам

Расскажи про любимый принцип из SOLID

1.0 Junior🔥 221 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой любимый принцип SOLID: Single Responsibility Principle

Из всех пяти принципов SOLID я выбираю Single Responsibility Principle (SRP) — принцип единственной ответственности. Для Frontend-разработчика этот принцип критически важен и даёт наибольший прирост в качестве кода.

Что это такое?

Single Responsibility Principle гласит: каждый модуль, класс или компонент должен иметь ровно одну причину для изменения. Иначе говоря, компонент должен отвечать ровно за одно — либо за отображение (View), либо за логику (Logic), либо за работу с данными (Data).

Почему это важно для фронтенда?

На фронтенде разработчики часто создают монолитные компоненты, которые одновременно:

  • Загружают данные с сервера
  • Обрабатывают состояние
  • Отображают UI
  • Обрабатывают события пользователя

Такие компоненты трудно тестировать, переиспользовать и менять.

// ❌ Плохо: компонент отвечает за всё
function UserProfile() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Загрузка данных
    fetch("/api/user")
      .then(r => r.json())
      .then(data => setUser(data))
      .catch(e => setError(e));
  }, []);

  // Обновление пользователя
  const handleUpdate = async (newData) => {
    const response = await fetch("/api/user", {
      method: "PUT",
      body: JSON.stringify(newData)
    });
    // ... обновление состояния
  };

  // Логика валидации
  const isEmailValid = user?.email?.includes("@");

  // Всё в одном компоненте!
  return (
    <div>
      {loading && <Spinner />}
      {error && <ErrorMessage error={error} />}
      {user && (
        <form>
          <input value={user.name} />
          <input value={user.email} />
          <button onClick={() => handleUpdate({...user})}>
            Save
          </button>
        </form>
      )}
    </div>
  );
}

Правильное применение SRP

Разделим ответственность на несколько слоёв:

// 1. Хук для работы с данными (Data Layer)
function useUser(userId) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchUser(userId)
      .then(setUser)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [userId]);

  const updateUser = async (newData) => {
    const updated = await updateUserAPI(userId, newData);
    setUser(updated);
  };

  return { user, loading, error, updateUser };
}

// 2. Утилита для валидации (Business Logic)
const userValidation = {
  isEmailValid: (email) => email?.includes("@"),
  isNameValid: (name) => name?.length > 2,
};

// 3. Компонент только для отображения (Presentation)
function UserProfileForm({ user, onUpdate, isLoading }) {
  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    onUpdate(Object.fromEntries(formData));
  };

  if (isLoading) return <Spinner />;

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" defaultValue={user.name} />
      <input name="email" defaultValue={user.email} />
      <button type="submit">Save</button>
    </form>
  );
}

// 4. Контейнер, который собирает всё вместе
function UserProfileContainer({ userId }) {
  const { user, loading, error, updateUser } = useUser(userId);

  if (error) return <ErrorMessage error={error} />;
  if (!user) return null;

  return (
    <UserProfileForm
      user={user}
      onUpdate={updateUser}
      isLoading={loading}
    />
  );
}

Преимущества SRP

  1. Простота тестирования: каждый компонент отвечает за одно, его легче мокировать
// ✅ Просто тестировать UserProfileForm
describe("UserProfileForm", () => {
  it("submits form data", () => {
    const mockUser = { name: "John", email: "john@example.com" };
    const mockOnUpdate = jest.fn();

    render(
      <UserProfileForm user={mockUser} onUpdate={mockOnUpdate} />
    );

    fireEvent.change(screen.getByDisplayValue("John"), {
      target: { value: "Jane" }
    });
    fireEvent.click(screen.getByText("Save"));

    expect(mockOnUpdate).toHaveBeenCalled();
  });
});
  1. Переиспользование: компоненты можно использовать в разных контекстах
// Одна форма для редактирования профиля
<UserProfileForm user={user} onUpdate={updateUser} />

// Та же форма для создания нового пользователя
<UserProfileForm user={emptyUser} onUpdate={createUser} />
  1. Поддерживаемость: изменения в одной части не влияют на другие
// Можем изменить API без тронутия компонента UI
async function fetchUser(userId) {
  // Была fetch, теперь GraphQL
  const query = gql`query { user(id: $userId) { name email } }`;
  return apolloClient.query({ query, variables: { userId } });
}

// UserProfileForm не требует изменений!
  1. Переиспользование логики: хук useUser работает везде
// В разных компонентах
function UserHeader() {
  const { user } = useUser(userId);
  return <h1>{user.name}</h1>;
}

function UserStats() {
  const { user } = useUser(userId);
  return <div>Email: {user.email}</div>;
}

Практический пример: список комментариев

// ✅ Правильно: разделение ответственности

// 1. Логика загрузки
function useComments(postId) {
  const [comments, setComments] = useState([]);
  useEffect(() => {
    loadComments(postId).then(setComments);
  }, [postId]);
  return comments;
}

// 2. Отображение одного комментария
function CommentItem({ comment }) {
  return (
    <div className="comment">
      <strong>{comment.author}</strong>
      <p>{comment.text}</p>
    </div>
  );
}

// 3. Список комментариев
function CommentsList({ comments, isLoading }) {
  if (isLoading) return <Spinner />;
  return <div>{comments.map(c => <CommentItem key={c.id} comment={c} />)}</div>;
}

// 4. Контейнер
function PostComments({ postId }) {
  const comments = useComments(postId);
  return <CommentsList comments={comments} />;
}

Заключение

Single Responsibility Principle — основа чистого фронтенд-кода. Компоненты, которые делают одно и делают это хорошо, легче понимать, тестировать и переиспользовать. Это напрямую снижает количество багов и упрощает добавление новых функций.