В чем разница между mixin и extension в Dart?

Ответ

Mixin — это способ внедрения функциональности в несколько классов без использования наследования. Миксины могут содержать методы, геттеры, сеттеры и даже поля с состоянием. Они подключаются к классу с помощью ключевого слова with.

mixin ValidationMixin {
  String? _email;

  bool isValidEmail(String email) {
    return RegExp(r'^[^@]+@[^@]+.[^@]+').hasMatch(email);
  }

  void setEmail(String email) {
    if (isValidEmail(email)) _email = email;
  }
}

class User with ValidationMixin {
  void register(String email) {
    setEmail(email);
    print('Email set: $_email');
  }
}

Extension — позволяет добавлять новые методы к существующим типам, даже к типам из сторонних библиотек или встроенным типам Dart. Extensions не могут добавлять поля с состоянием и не изменяют оригинальный класс.

extension DateTimeExtension on DateTime {
  String toFormattedString() {
    return '${day.toString().padLeft(2, '0')}.${month.toString().padLeft(2, '0')}.$year';
  }
}

void main() {
  final now = DateTime.now();
  print(now.toFormattedString()); // Например: 15.04.2024
}

Ключевые различия:

  1. Назначение: Mixin для повторного использования кода с состоянием в иерархии классов, extension для добавления utility-методов к существующим типам.
  2. Состояние: Mixin может содержать поля (состояние), extension — нет.
  3. Использование: Mixin применяется с with при объявлении класса, extension методы доступны автоматически для экземпляров типа.
  4. Ограничения: Класс может использовать несколько mixins, но extension не может переопределять существующие методы типа.

Ответ 18+ 🔞

Давай разберёмся, что тут у нас за дичь. Представь, что ты пришёл в бар, а тебе вместо пива подали два стакана — в одном какая-то мутная жижа, в другом газировка. И оба вроде как жидкость, но суть-то разная, ёпта!

Вот смотри, Mixin — это как будто ты взял готовый модуль от какого-нибудь китайского скутера и впендюрил его в свой велик. У модуля есть свои провода, своя хуйня, своё состояние. Ты его просто прикрутил, и теперь твой велик может сигналить и моргать фарами. В коде это выглядит так:

mixin ValidationMixin {
  String? _email; // Вот, видишь? Состояние! Поле есть!

  bool isValidEmail(String email) {
    return RegExp(r'^[^@]+@[^@]+.[^@]+').hasMatch(email);
  }

  void setEmail(String email) {
    if (isValidEmail(email)) _email = email;
  }
}

class User with ValidationMixin { // Ключевое слово `with` — это наш гаечный ключ
  void register(String email) {
    setEmail(email);
    print('Email set: $_email');
  }
}

То есть ты берёшь класс User и говоришь: «Слушай, дружок, ты теперь ещё и валидацию почты умеешь, вот тебе методы и даже поле _email впридачу». Это мощно, это как дать человеку третью руку. Состояние — это наше всё.

А теперь Extension — это вообще другая история, ядрёна вошь! Это как если бы ты взял обычную отвертку и намагнитил её жопой. Сама отвертка не изменилась, завод-изготовитель даже не в курсе. Но теперь она умеет держать винтики! Состояния у неё нового не появилось, просто функционал добавился.

extension DateTimeExtension on DateTime { // Сказали: «Всем DateTime отныне — новый метод!»
  String toFormattedString() {
    return '${day.toString().padLeft(2, '0')}.${month.toString().padLeft(2, '0')}.$year';
  }
}

void main() {
  final now = DateTime.now();
  print(now.toFormattedString()); // И всё, просто пользуешься, как будто так и было
}

Видишь разницу? Ты не лезешь внутрь класса DateTime, не пересобираешь его на заводе. Ты просто со стороны объявляешь: «Эй, все даты! Отныне вы умеете в toFormattedString()». И они начинают уметь. Волшебство, блядь!

Так в чём же, блядь, соль? Давай списком, а то голова уже пухнет:

  1. Зачем нужны? Миксин — это когда тебе надо впихнуть в класс кусок логики со своим внутренним миром (полями, состоянием). Экстеншн — это когда ты хочешь просто добавить удобную фигню (типа форматирования даты) к уже существующему типу, не трогая его кишки. Доверия к исходному классу — ебать ноль, поэтому лезем через extension.

  2. Про состояние. Это главный камень преткновения! Миксин может иметь поля (как _email). Экстеншн — ни-ху-я! Только методы, геттеры, сеттеры. Никакого своего состояния. Нельзя добавить к int поле _isPretty. Не выйдет.

  3. Как юзать. Миксин ты примешиваешь к классу в момент его объявления (class User with ValidationMixin). Это раз и навсегда. Экстеншн же включается автоматом, как только ты его импортируешь. Написал extension — и все переменные этого типа в твоём файле сразу поумнели.

  4. Ограничения, ёб твою мать. Миксинов можно навешать несколько штук на один класс — будет как франкенштейн, но рабочий. Экстеншн не может переопределять существующие методы класса. Не вздумай через extension переписать стандартный toString() у того же DateTime — компилятор тебя послать нахуй и не покраснеет. Он только добавляет новое, старое не трогает.

Короче, если нужно расширить функциональность с памятью — бери миксин. Если нужно научить старую собаку новым трюкам, но без переделки конуры — твой выбор extension. Всё просто, как три копейки.