Почему не нужно применять принципы SOLID?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Почему не нужно применять принципы SOLID?
Это интересный вопрос. На самом деле, SOLID принципы НЕ всегда плохие - но их часто неправильно применяют, особенно в frontend разработке. Давайте разберемся, когда SOLID становится вредоносным.
Когда SOLID приносит вред
1. Over-engineering (Оверинжиниринг)
Проблема: Разработчик создает абстракции "на будущее", которые никогда не используются.
// ПЛОХО - over-engineering
interface UserRepository {
getUser(id: string): Promise<User>;
saveUser(user: User): Promise<void>;
deleteUser(id: string): Promise<void>;
}
class PostgresUserRepository implements UserRepository {
// ...
}
class MockUserRepository implements UserRepository {
// ...
}
// Затем тонна код-боилеркоада просто для примерно равного хранилища
Что происходит:
- Код становится сложнее
- Разработчики тратят время на абстракции
- Сложнее понять логику (нужно прыгать между файлами)
- Абстракции часто не нужны (никогда не меняются)
Когда это реально нужно: Только если у вас РЕАЛЬНО есть несколько реализаций или планы их добавить.
2. Множество маленьких файлов
Проблема: SOLID требует разделения ответственности, но это приводит к паралич паралляизма.
// ПЛОХО - too much separation
// src/components/User/
// - UserContainer.tsx
// - UserPresenter.tsx
// - UserViewModel.ts
// - useUserFetch.ts
// - UserTypes.ts
// - UserUtils.ts
// - UserStyles.ts
// - __tests__/
// Для простого компонента из 50 строк создали 7 файлов!
Проблемы:
- Когда что-то сломалось, нужно исправить везде
- Сложно найти, где нужный код
- Время на навигацию и редактирование увеличивается
- Pull request'ы огромные
3. DI (Dependency Injection) в React
Проблема: Inject dependencies через функции приводит к паралич на клиенте.
// ПЛОХО - DI в React
interface ApiService {
getUsers(): Promise<User[]>;
}
interface UserListProps {
apiService: ApiService;
}
const UserList = ({ apiService }: UserListProps) => {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
apiService.getUsers().then(setUsers);
}, [apiService]);
return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};
// Использование требует
<UserList apiService={new ApiService()} />
// Или даже хуже - глобальный контекст
const ApiContext = createContext<ApiService | null>(null);
const UserList = () => {
const apiService = useContext(ApiContext);
if (!apiService) throw new Error('Missing provider');
// ...
};
Почему это плохо в React:
- React уже имеет встроенный DI через props
- Context для этого слишком мощен
- Сложнее тестировать (нужны провайдеры)
- Код становится более имплицитным
4. Принцип Open/Closed vs Простота
Проблема: Делаешь код открытым для расширения, но это усложняет текущий код.
// ПЛОХО - OCP over-engineering
interface Theme {
applyButton(): string;
applyInput(): string;
applyHeader(): string;
// + 20 других методов
}
class DarkTheme implements Theme { /* ... */ }
class LightTheme implements Theme { /* ... */ }
// Использование
const applyTheme = (theme: Theme) => {
// Нужно знать все методы Theme интерфейса
};
Зачем это нужно? Если есть больше 2-3 тем. Иначе это просто overkill.
Почему SOLID часто вредит в frontend
Причина 1: Frontend часто меняется
Жизненный цикл backend'а:
В 2020 году создали API -> работает с небольшими изменениями до 2024
Абстракции приносят пользу
Жизненный цикл frontend'а:
В 2024 году переписали на React -> 2024 переписали на Vue -> 2025 переписали на Svelte
Абстракции устаревают
Причина 2: React уже хорошо решает많은 проблем
// React уже дает инверсию управления через JSX
<UserList
users={users}
onUserClick={handleUserClick}
renderItem={item => <CustomItem {...item} />}
/>
// Нужна ли еще одна слой DI?
Причина 3: Тестирование в React проще, чем в backend
// Jest + React Testing Library - очень просто тестировать
test('UserList shows users', () => {
render(<UserList />);
expect(screen.getByText('John')).toBeInTheDocument();
});
// Не нужны моки и сложные DI контейнеры
Когда SOLID приносит пользу
1. Большой проект (50+ компонентов)
// Single Responsibility имеет смысл
const UserCard = ({ user }) => <div>{user.name}</div>;
const UserList = ({ users }) => users.map(u => <UserCard user={u} />);
const UserPage = () => {
const [users, setUsers] = useState([]);
// ...
return <UserList users={users} />;
};
// Каждый компонент - одна ответственность
// Легко найти и изменить
2. Общие утилиты
// DRY + SOLID имеет смысл для утилит
const formatDate = (date: Date): string => date.toLocaleDateString();
const formatCurrency = (amount: number): string => `$${amount.toFixed(2)}`;
const formatTime = (time: Date): string => time.toLocaleTimeString();
// Переиспользуется везде
import { formatDate, formatCurrency } from '@/lib/formatters';
3. Множество реализаций
// Interface имеет смысл, если РЕАЛЬНО есть 2+ реализации
interface HttpClient {
get<T>(url: string): Promise<T>;
post<T>(url: string, data: unknown): Promise<T>;
}
class FetchHttpClient implements HttpClient { /* ... */ }
class AxiosHttpClient implements HttpClient { /* ... */ }
class MockHttpClient implements HttpClient { /* ... */ }
// Тестирование
const httpClient = new MockHttpClient();
const userService = new UserService(httpClient);
test('UserService fetches users', async () => {
const users = await userService.getUsers();
expect(users.length).toBe(2);
});
Правильный подход: SOLID light
1. Начни с простого
// v1 - Просто работает
const UserList = () => {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers);
}, []);
return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};
2. Если усложняется - рефакторь
// v2 - Выделил hook
const useUsers = () => {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
fetch('/api/users').then(r => r.json()).then(setUsers);
}, []);
return users;
};
const UserList = () => {
const users = useUsers();
return <div>{users.map(u => <div>{u.name}</div>)}</div>;
};
3. Если нужна гибкость - добавь абстракцию
// v3 - API абстрактен
const useUsers = (apiClient = fetch) => {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
apiClient('/api/users').then(setUsers);
}, [apiClient]);
return users;
};
Заключение
Не нужно применять SOLID, если:
- Проект маленький (< 20 компонентов)
- Код не меняется часто
- Реальной нет множественных реализаций
- SOLID усложняет код больше, чем помогает
Нужно применять SOLID, если:
- Проект большой и растет
- Часто добавляют новые фичи
- Реально есть несколько реализаций
- SOLID упрощает кодовую базу
Правило: Начни с простого (KISS), затем рефакторь когда понадобится (TDD). SOLID должен служить гайдом, а не догме.
Хороший разработчик знает SOLID и применяет его когда это имеет смысл, а не всегда.