Что означает принцип DRY в разработке на Dart/Flutter?

Ответ

DRY (Don't Repeat Yourself — «Не повторяйся») — это фундаментальный принцип разработки ПО, который гласит: «Каждое знание должно иметь единственное, однозначное, авторитетное представление в системе». На практике это означает устранение дублирования логики, стилей или структуры кода.

Проблема дублирования в Flutter-проекте:

// ❌ Нарушение DRY: одинаковая стилизация кнопки в разных местах
ElevatedButton(
  onPressed: () {},
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    foregroundColor: Colors.white,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  ),
  child: Text('Save'),
)
// ... и тот же самый стиль повторяется для кнопки 'Submit', 'Confirm' и т.д.

Решения в соответствии с DRY:

  1. Вынос в константу или метод:

    // ✅ Единый источник стиля
    final ButtonStyle myPrimaryButtonStyle = ElevatedButton.styleFrom(
      backgroundColor: Colors.blue,
      foregroundColor: Colors.white,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    );
    
    // Использование
    ElevatedButton(style: myPrimaryButtonStyle, onPressed: () {}, child: Text('Save')),
    ElevatedButton(style: myPrimaryButtonStyle, onPressed: () {}, child: Text('Submit')),
  2. Создание кастомного виджета:

    // ✅ Кастомная кнопка с инкапсулированной логикой и стилем
    class PrimaryButton extends StatelessWidget {
      final String label;
      final VoidCallback onPressed;
      const PrimaryButton({super.key, required this.label, required this.onPressed});
    
      @override
      Widget build(BuildContext context) {
        return ElevatedButton(
          onPressed: onPressed,
          style: myPrimaryButtonStyle, // Используем константу из примера выше
          child: Text(label),
        );
      }
    }
    // Теперь изменение стиля всех кнопок происходит в одном месте.
  3. Использование тем (Theming) в Flutter:

    // ✅ Глобальное определение в ThemeData
    MaterialApp(
      theme: ThemeData(
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: myPrimaryButtonStyle,
        ),
      ),
    );

Почему DRY критически важен:

  • Снижает количество ошибок: Исправление или улучшение логики требуется только в одном месте.
  • Упрощает поддержку: Код становится чище и понятнее.
  • Повышает согласованность: UI и поведение приложения единообразны.

Осторожно: Слепое следование DRY может привести к излишней абстракции и сложным зависимостям. Иногда небольшое, осознанное дублирование («прагматичное дублирование») лучше, чем неправильная абстракция.

Ответ 18+ 🔞

Давай разберем этот ваш принцип DRY, а то смотрю — народ как обезьянки, один и тот же код копирует, а потом охуевает, когда надо в ста местах правки вносить. Ёпта, ну вы даёте.

Смотри, суть проще пареной репы: «Каждое знание — один раз и в одном месте». Представь, что у тебя в коде как в плохой коммуналке — один сосед (кусок логики) рассказал анекдот, второй его пересказал криво, третий вообще забыл концовку. И когда оригинал меняется, начинается пиздец и волнение ебать. Все сидят с разными версиями. DRY — это выселить всех в отдельную квартиру и поставить на стену один общий телевизор с актуальной новостью.

Вот тебе наглядный пиздец в проекте на Flutter:

// ❌ Нарушение DRY: пиздец как не надо делать. Один и тот же стиль кнопки раскидан везде.
ElevatedButton(
  onPressed: () {},
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    foregroundColor: Colors.white,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  ),
  child: Text('Save'),
)
// ... и этот же уёбищный стиль ты встретишь ещё в двадцати файлах. Полный распиздяй.

Как по-человечески решить, чтобы не было мучительно больно:

  1. Выкинул в константу — и спи спокойно.

    // ✅ Один источник правды, один хозяин. Меняешь здесь — обновляется везде.
    final ButtonStyle myPrimaryButtonStyle = ElevatedButton.styleFrom(
      backgroundColor: Colors.blue,
      foregroundColor: Colors.white,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    );
    
    // Использование — красота, а не код.
    ElevatedButton(style: myPrimaryButtonStyle, onPressed: () {}, child: Text('Save')),
    ElevatedButton(style: myPrimaryButtonStyle, onPressed: () {}, child: Text('Submit')),
  2. Заверни всё в свой виджет — вообще красота.

    // ✅ Кастомная кнопка. Всё зашито внутри, снаружи только текст и действие.
    class PrimaryButton extends StatelessWidget {
      final String label;
      final VoidCallback onPressed;
      const PrimaryButton({super.key, required this.label, required this.onPressed});
    
      @override
      Widget build(BuildContext context) {
        return ElevatedButton(
          onPressed: onPressed,
          style: myPrimaryButtonStyle, // Берём ту самую константу
          child: Text(label),
        );
      }
    }
    // Теперь хоть завтра дизайнеру в голову ебнет поменять все скругления — ты поправишь в ОДНОМ месте и ни о чём не забудешь.
  3. Если проект большой — тема твой лучший друг.

    // ✅ Глобально прописал в теме — и все кнопки в приложении стали послушными.
    MaterialApp(
      theme: ThemeData(
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: myPrimaryButtonStyle,
        ),
      ),
    );

А зачем этот весь цирк, спросишь?

  • Меньше косяков: Баг пофиксил в одном месте — он автоматом чинится везде. Никаких «ой, я же забыл в том файле поправить».
  • Поддержка — не ад: Приходит новый разработчик, смотрит — а, стили кнопок вот тут лежат. А не бегает, как угорелый, по всем экранам.
  • Всё единообразно: Приложение не выглядит как солянка из кусков, сделанных впятером в слепую.

Но! Тут главное — не перестараться. Иногда видишь, как человек ради DRY создаёт такую ебанутую абстракцию с кучей условий и параметров, что проще было десять раз скопировать. Это уже не принцип, а пиздопроебина. Иногда немного осознанного дублирования лучше, чем кривая, насильственная абстракция. Чувствуй грань.

Видео-ответы