Ответ
В разработке на Flutter я активно применяю принципы SOLID, DRY и KISS, адаптируя их под специфику фреймворка и языка Dart.
SOLID в контексте Flutter/Dart:
-
Принцип единственной ответственности (SRP): Каждый класс или виджет должен решать одну задачу. Например, я разделяю
StatefulWidgetна:- Виджет-экран, который собирает UI.
- Отдельный класс-логики (
ChangeNotifier,Bloc,Cubit), который управляет состоянием и бизнес-логикой. - Сервис-класс для работы с API или базой данных.
-
Принцип открытости/закрытости (OCP): Код должен быть открыт для расширения, но закрыт для изменений. Во Flutter это достигается через композицию виджетов, а не наследование. Вместо того чтобы модифицировать сложный виджет, я создаю новый, который оборачивает или комбинирует существующие.
// Вместо модификации кнопки, создаю новый виджет-обертку. class IconButtonWithLabel extends StatelessWidget { final IconData icon; final String label; final VoidCallback onPressed; const IconButtonWithLabel({...}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ IconButton(icon: Icon(icon), onPressed: onPressed), Text(label), ], ); } } -
Принцип подстановки Барбары Лисков (LSP): Подклассы должны быть взаимозаменяемы с родительскими классами. В Dart это означает корректное использование
@overrideи соблюдение контрактов абстрактных классов (например,WidgetилиState). -
Принцип разделения интерфейса (ISP): Вместо одного "жирного" абстракного класса с множеством методов лучше создать несколько специфичных интерфейсов (в Dart —
abstract class). Например, отдельный интерфейс для локального кэша и отдельный — для сетевого клиента. -
Принцип инверсии зависимостей (DIP): Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. На практике я использую dependency injection (через
get_it,riverpodили конструкторы) и завишу от абстракций (Repository,DataSource), а не от их конкретных реализаций (ApiClient,LocalStorage).
DRY (Don't Repeat Yourself): Повторяющуюся логику (валидация форм, форматирование дат, стили виджетов) я выношу в утилитные классы, extensions или общие виджеты-компоненты.
KISS (Keep It Simple, Stupid): Я стремлюсь к простым и понятным решениям. Например, предпочитаю StatelessWidget и StatefulWidget там, где достаточно, и не усложняю архитектуру (например, не внедряю Bloc для простого экрана с одной кнопкой).
Ответ 18+ 🔞
Э, слушай, вот сидишь ты, пишешь на Flutter, и вроде всё летает, но код через месяц самому себе напоминает лапшу, которую кот сука собака навертел. А всё потому, что эти ваши принципы, SOLID там всякие, многие воспринимают как священное писание для Java-дедов, а не как реально полезные штуки. Давай разберём на пальцах, без овердохуища теории.
Ну, SOLID, ёпта. Не для галочки, а чтобы голова не болела.
Принцип единственной ответственности (SRP). Это когда у тебя виджет не пытается быть богом и царём. Он не должен и UI рисовать, и в сеть ходить, и в базу данные пихать. Это пиздопроебибна получается. Отделяй мух от котлет: пусть экран (StatefulWidget) только собирает интерфейс из кубиков, вся бизнес-логика и состояние живут в отдельной сущности — в Cubit, ChangeNotifier или где ты там привык. А за общение с внешним миром отвечает вообще отдельный сервисный класс. Каждый делает своё дело, и когда надо что-то поменять, ты не перелопачиваешь тонны кода, а идёшь в одно конкретное место. Доверия ебать ноль к виджету, который делает всё.
Принцип открытости/закрытости (OCP). Звучит заумно, а на деле — композиция, а не наследование. Не лезь ты внутрь готового сложного виджета, чтобы прикрутить ему какую-то мелкую фичу. Оберни его в другой! Создай новый виджет-обёртку, который будет использовать старый как кирпичик. Вот смотри, простой пример: нужна кнопка с иконкой и подписью.
// Не надо ковырять стандартную IconButton. Просто сделай свою.
class IconButtonWithLabel extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onPressed;
const IconButtonWithLabel({...});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(icon: Icon(icon), onPressed: onPressed), // Используем готовое
Text(label), // Добавляем своё
],
);
}
}
Всё. Старый код не тронут, новая функциональность есть. Красота.
Принцип подстановки Лисков (LSP). Ну, тут без сюрпризов: если ты наследуешься от какого-то класса или реализуешь интерфейс, будь добр играть по его правилам. Переопределяй методы (@override) так, чтобы твой класс можно было впихнуть куда угодно вместо родительского, и ничего не сломалось. Если твой кастомный Widget ломает всё приложение — ты пидарас шерстяной, а не архитектор.
Принцип разделения интерфейса (ISP). Не создавай интерфейс-монстра, который заставляет класс реализовывать кучу ненужных ему методов. Лучше несколько маленьких и точных. Ну, например, abstract class для работы с кэшем и отдельный — для сетевых запросов. Тогда класс, который только кэширует, не будет вынужден реализовывать метод fetchFromCloud(). Иначе это манда с ушами получается.
Принцип инверсии зависимостей (DIP). Самый, наверное, важный для тестирования и гибкости. Высокоуровневые модули (твой экран, твоя бизнес-логика) не должны знать конкретно, откуда берутся данные: из сети, из базы или из файла. Они должны зависеть от абстракции — от интерфейса Repository. А уже реализацию этого репозитория (ApiRepository, LocalRepository) ты прокидываешь извне, через dependency injection (get_it, riverpod или просто в конструктор). Получается, чтобы подменить настоящий API на мок для тестов, тебе нужно поменять конфигурацию в одном месте, а не переписывать пол-приложения. Волнение ебать сразу спадает.
DRY (Don't Repeat Yourself). Тут всё просто, как три копейки. Видишь, что одну и ту же хуйню (формат даты, проверку email, стиль кнопки) пишешь в десяти местах? Выноси нахуй! Сделай extension для String, вынеси общие стили в отдельный файл app_theme.dart, повторяющуюся логику — в утилитный класс. Иначе потом, когда поменяется дизайн или логика, тебе придётся бегать по всему проекту и править в пятидесяти файлах. Терпения ноль ебать на такое.
KISS (Keep It Simple, Stupid). И, наконец, золотое правило. Не усложняй там, где не надо. Не тащи Bloc с кучей событий и состояний на экран, где одна кнопка, которая меняет цвет. Используй StatefulWidget и не парься. Не создавай семиуровневую абстракцию для задачи из двух действий. Самый простой работающий код — лучший код. А то некоторые так заархитектуривают, что потом сами от своего кода охуевают. Помни: твой код должен быть понятен тебе же через полгода в три часа ночи, когда всё падает.
Видео-ответы
▶
▶
▶
▶
▶
▶