Как стать автором
Обновить

Комментарии 23

В статье мало наркомании)


А так было интересно.

Я правильно понимаю что миксин нельзя ограничить типами на которые он может применяться и из него никак не обратиться к методам и полям типа к которому он применен?

Можно и то, и другое. Например, вот так:


abstract class Animal {
  void run() {}
}

mixin SpeedRunner on Animal {
  void speedRun() {
    run();
    run();
  }
}

class Ostrich extends Animal with SpeedRunner {}

// Ostrich().speedRun();
О, спасибо, выглядит действительно довольно удобно, как прокачанные экстеншены из того же котлина.

Только в котлине или расте для этого не надо создавать новый тип как в данном случае. Можно имплементировать extension или trait сразу для любого типа, например, числового и вызвать метод: 1.foo(). В целом миксины похожи на них, но с уклоном в обычное множественно наследование и не такие могущественные.

Да, это я что то маху дал, пожалуй согласен с тем что оно совсем о разном. Экстеншены они о внешнем дополнении объекта новым поведением, но и только. Между собой два экстеншена уже не свяжешь, как контракт для аргумента не укажешь.
Миксины же о дополнении поведения и контракта изнутри, при этом так же позволяют использовать много методов и инкапсулировать их с данными миксина.

Ну наверное это ближе к растовым трейтам и хаскелевым тайпклассам. Но только в них нельзя хранить данные. Хотя имеет ли это большой смысл на самом деле? В таком случае можно просто разрешить множественное наследование классов в языке и не париться.

Множественное наследование тянет за собой diamond problem, так что от него уже все шугаются. Плюс не всегда нужна семантика is-a, миксины хорошо ложатся на текущий курс партии — composition over inheritance.


Когда полезно иметь данные в миксине — например в геймдеве. Если хочется чтобы объект имел свойства 'Serializable', 'Drawable', 'InventoryOwning' и 'Killable' (и ещё много чего), то реализовывать через множественное наследование — разлапистая иерархия получается, и изменять потом сложно. Легче набрать объект из миксинов, тем более что тесное взаимодействие с основным классом будет наоборот вредить гибкости.

Вышеперечисленные свойства похожи на обычные интерфейсы.

Похожи немного, да; ключевая разница — они с реализациями подмешиваются, так что мы сразу даём классу дополнительное обобщённое поведение (а не обязуемся новое специализированное поведение в классе реализовать). Взять в пример тот же Drawable — он может притащить с собой члены данных с графикой (которую мы можем назначить для каждого экземпляра свою) и код для отрисовки, а код класса к которому подмешиваем останется нетронут и даже не узнает, что класс внезапно научился себя отображать.

В Dart есть экстеншены:


extension SuperInt on int {
  List<int> to(int end) => List.generate(end - this + 1, (i) => this + i);
}

После этого можно делать так:


final range = 2.to(5); // range = [2, 3, 4, 5]
Можно обращаться, если явным образом указать через mixin A on B, где B — родительский класс.

gist.github.com/kevmoo/b60dc2fc7ea49acecb1fd2b57bf9be57
Спасибо, действительно удобно.
Серьезно? Вы пол статьи тратите на размышления по поводу логичности абстрактного примера из чужой статьи? На примеры стоит смотреть через призму того что они пытаются продемонстрировать, а не того, как можно было бы сделать это иначе. У примера с животными была конкретная цель — показать выгоду по сравнению с DDD. У примера с спортсменами, как раз то к чему вы пытались прийти. Для вынесения общего функционала, который может потребоваться в разных местах. В том числе и в ситуациях когда нет общего наследника, так и в ситуациях когда он есть. Как в ситуациях, когда это важное свойство, так и в ситуациях когда оно незначительно. Так что вывод «зачем вообще нужны mixin'ы» можно сделать как из моей статьи, так и из кучи англоязычных в сети, странно что у вас это не получилось.
К слову, вы пытались привести пример из флаттера, так вот в самом движке есть намного более яркий пример SingleTickerProviderStateMixin. Он как раз выносит важный функционал для воспроизведения анимаций в отдельный миксин, хотя используется этот миксин классамм с общим родителем, там даже ограничение стоит — «on State». Так что, по-моему скромному мнению, чтобы восполнить пробел существующих статей, нужно было сначала изучить их поподробнее, ну и естественно посмотреть реальные кейсы, которыми сам Flutter более чем богат.

Прошу прощения, если я Вас обидел

Вы меня совершенно не обидели. Вы не правильно поняли мой комментарий, судя по всему. Хотелось хотя бы более глубокого погружения в тему, раз уж вы решили восполнить пробелы информации в данной теме (хотя с этим я совершенно не согласен, информации более чем достаточно). А что на выходе — вы разбираете чужой пример выдернутый из контекста, потом приводите свой пример на основе которого делаете совсем не полный вывод, я бы даже сказал ошибочный, подавая его как правильный юзкейс. Самое правильное что вы написали, так это последний абзац. Вариантов использования в различных ситуациях можно придумывать до бесконечности. А учитывая, что вы хотели дать ответ на вопрос, когда же лучше использовать данный механизм, гораздо полезнее было бы уж тогда хотя бы рассмотреть кучу разных кейсов, при том рассмотреть их подробно. А в итоге статья получается не выполняет даже ту цель, которую вы поставили перед ней сами вначале. Удачи вам в последующих статьях, надеюсь вы здраво воспримете мою критику.

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

Допустим все предыдущие статьи плохо охватывали материал и вы решили восполнить это, вот конкретная ваша фраза:
в каких случаях их использование более предпочтительно, чем обычное наследование или реализация интерфейсов. Эта статья является попыткой восполнить этот пробел.


В итоге вы приводите всего 1 пример и делаете вывод:

если имеется несколько различных иерархий, которым нужно добавить один и тот же функционал, определяющий некоторое несущественное свойство для сущностей этих иерархий. Либо это может быть одна иерархия, но мы имеем дело с разными ее ветками.


То есть вынесение важной части функционала, которая по факту одинакова у обоих элементов вы считаете не возможным?

Киллер и биатлонист, оба стреляют у обоих это важное свойство, невозможно юзать миксин?

Слишком абстрактно?

Я привел пример, который вы судя по всему даже не открыли, раз пишите что я не представляю вам конструктивной критики.
Вы указали как тег своей статьи Флаттер в том числе. Наверное реализации в самом Флаттере тоже могут быть аргументом в таком случае.
Я привел вам пример SingleTickerProviderStateMixin это довольно важная часть функционала по реализации анимаций, и это миксин можно прицепить к различным стейтам.
Анимация для визуального класса не значимая вещь?

И да, я не предлагаю все подряд выносить в миксины, это то еще зло в красивой обертке. Любым инструментом надо пользоваться аккуратно и к месту, можно и фугасом попытаться гвоздь забить, может получиться. А может и нет.

Следующий момент, касаемо целей — вы поставили цель восполнить пробел, ну так показали бы разные кейсы, а то выходит кейс 1 со спорным выводом. Так и позиционировали бы статью как один из кейсов который мне кажется правильным.

А по поводу критики моего примера, я и не приводил его как вариант реального использования или великолепной архитектуры, я приводил его как абстрактный пример, для демонстрации конкретной ситуации, то что вы этого не увидели и захотели в нем видеть что-то другое, это как говорил один футболист ваши ожидания и ваши проблемы.

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


abstract class Shooter {
  void shoot();
}

class Killer extends Shooter {}

class Biathlonist extends Shooter {}

И именно то, что ни киллер, ни биатлонист без стрельбы не могут существовать, говорит о том, что использование примесей в данном случае не является обоснованным. Примесь хороша тогда, когда объект остается самим собой, независимо от того, есть свойство, реализованное в примеси или нет.


Также не нужно путать важное свойство и существенное свойство. Свойство может быть важным, но не существенным. В частности в приведенном Вами примере с SingleTickerProviderStateMixin. Этот mixin безусловно добавляет важное свойство к виджету, но не существенное, так как при наличии или отсутствии этого свойства виджет остается виджетом. А вот если у виджета убрать или добавить существенное свойство, то он либо перестанет быть виджетом вообще, либо станет принципиально другим виджетом (примером принципиально разных виджетов являются StatelessWidget и StatefulWidget)


Поэтому приведенный мной кейс работает и в данном случае. И к нему следует относится не как к единственному частному случаю, а как к некоторому обобщению, в рамки которого укладываются все варианты использования примесей (ну или почти все).

Только вот незадача, киллер помимо того что Shooter он еще и Bandit и немного болтает на фене, как и Thief который от того же Bandit наследуется, но предвкушая наследование бандита от стрелка, Thief очень искусный щипач и с его слов «в гробу он эту стрельбу видел». А биатлонист, который Shooter и еще спортсмен, который по дефолту имеет метод тренироваться и хранит медальки. А еще у нас есть стрелок стендовик, который тоже спортсмен и тоже стреляет, но увы без лыж, говорит не удобно ему в них стрелять. И бухгалтер Оля, которая в свободное время любит ходить в тир, и жизни своей без того чтоб засадить фигурке медведя куда-нибудь не представляет. И да, конечно же у нас есть интерфейсы, я не оспариваю что интерфейсы это хорошо, это очень удобно и сам их юзаю. Пусть в жизни они все стреляют по разному, кто-то человечков валит, кто-то в мишени палит, а кто-то по тарелочкам садит. Но вот что если на уровне абстракции который выбран нами, вся эта стрельба имеет абсолютно одинаковую реализацию. Мы получаем дубль абсолютно одинакового кода, который мы пишем реализуя интерфейсы. Так что функционал существенный, но для всех один и тот же. Кстати использование примеси не исключает использование интерфейсов, это к слову.

А касаемо примера из Флаттера, ну вы бы хотя бы открыли что ли его для приличия. Ну право, он ограничение на State имеет, ну причем здесь виджеты. И юзается в куче его наследников, в том числе те которые напрямую относятся к стейтам для которых анимация не важное а существенное свойство, например ImplicitlyAnimatedWidgetState. Это даже из названия следует. И в таких для которых это не является существенным свойством, например _HelperErrorState. Видимо команде флаттера надо как можно скорее переписывать эту часть, ведь она не покрывается вашим случаем на все случаи жизни.

Вы очень любите примеры крайне далекие от реальной разработки. Поэтому я не считаю необходимым разгребать все, что Вы написали и тратить время на пустую дискуссию.


ну причем здесь виджеты

Виджеты здесь при том, что речь идет о состоянии виджета.


К сожалению вместо конструктивного обсуждения Вы опять скатились на раздраженно-снисходительный тон, который считаю неуместным в подобных обсуждениях. Уверяю Вас, что у меня достаточный опыт разработки, чтобы суметь обосновать сказанное в статье. Но с Вашей стороны я не вижу потребности это понять, а у меня нет никакого желания общаться в таком тоне. Поэтому со своей стороны я заканчиваю эту дискуссию. Пусть каждый остается при своем мнении.

Куда уж дальше от реальной разработки, чем тот же пример из флаттера. Согласен, лучше закончить эту дискуссию, видимо Вы из тех зашоренных людей, у которых все вокруг «неправы, один он Д'Артаньян».
Но с Вашей стороны я не вижу потребности это понять

Зря Вы считаете, что я не понимаю то что вы написали, у меня тоже достаточно опыта реальной разработки. И я не пытаюсь доказать что пример ваш не правилен, я пытался Вам показать, что возведение в абсолют чего-либо это неправильная позиция, и узкость. Но увы, видимо Вы не имеете нужды это увидеть, Вам в своём мирке уютно, и ничего кроме него Вы не хотите видеть. Это Ваше право. На этом всё, в одном мы сошлись — каждый при своём.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.