флейм о трейтах

AmdY

Пью пиво
Команда форума
Knayz, множественное наследование не делают из-за возможных конфликтов, посмотри на http://php.net/trait

PHP:
<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>
Абстрактный класс без реализации действительно может заменять интерфейсы, но интерейс гарантирует, что реализации не будет. Мне, кстати, абстрактные классы вместо интерфейсов больше по душе, но это из-за неидеальности жизни.
 

Активист

Активист
Команда форума
Knayz, множественное наследование не делают из-за возможных конфликтов, посмотри на http://php.net/trait
...
Абстрактный класс без реализации действительно может заменять интерфейсы, но интерейс гарантирует, что реализации не будет. Мне, кстати, абстрактные классы вместо интерфейсов больше по душе, но это из-за неидеальности жизни.
Может не стоит смешивать понятия наследования и трейты? А то у людей мозг взорвется. Трейты не являются механизмами наследования, и не могут рассматриваться как наследование ни при каких условиях. Хотя функцию class_uses можно применить для определения наличия того или иного трейта в объекте, все же наследования там нет и методы описанные в трейте нельзя переопредить. Только в крайних случах можно использовать as/insteadof для переопределения методов, но это на мой взгляд вообще является плохим тоном, и использовать их нельзя. Лучше уж тогда заюзать pre/post dispatch методы, чем переопределять конфликты.
 

AmdY

Пью пиво
Команда форума
Может не стоит смешивать понятия наследования и трейты?
ага, это возможно лишнее, на этапе обучения лучше придерживаться классики, чтобы всё в ком грязи не смешалось
 

hell0w0rd

Продвинутый новичок
и методы описанные в трейте нельзя переопредить.
да щас, берешь и переопределяешь. Нельзя заместить методы одного трейта методами другого, без insteadof. Также нельзя переопределить св-ва трейта.
 

Активист

Активист
Команда форума
да щас, берешь и переопределяешь. Нельзя заместить методы одного трейта методами другого, без insteadof. Также нельзя переопределить св-ва трейта.
Ага, щас. Переопределить != заменить.

PHP:
<?php
trait fooo
{
    public function bar()
    {
        print $this->bar + 2;
    }
}

class foo
{
    use fooo;
  
    public function bar()
    {
        parent::bar();
    }
}

(new foo)->bar();
PHP Fatal error: Cannot access parent:: when current class scope has no parent in /var/www/avtovokzal.localhost/httpdocs/test.php on line 16
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
В педивикии написано хрени, чуть более, чем 9000%, потому не соглашусь. Трейты это имхо именно эмуляция много-наследования.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
как раз таки в лярве SoftDeletingTrait - вполне себе корректный юзкейс для трейта.
 

Absinthe

жожо
ORM лярвы — это в принципе говно.
Есть что-нибудь лучше для AR? :)

Про отсутствие чего-то нормального для реализации Data Mapper и вовсе молчу: ни одной библиотеки нет. Doctrine, из которой в каждой строке изнутри лезут warning'и просто не в счет из-за качества. Точнее, его полного отсутствия.
 

Вурдалак

Продвинутый новичок
Ну конечно, а еще седаны - полное говно, ведь на них даже тонну картошки перевезти нельзя :D
AR — это не седан, а картошкомобиль: тут тебе и картошка, и доставка. Картофель-мутант. Или автомобиль-мутант. Я пока не определился.
 

Вурдалак

Продвинутый новичок
А трейты просто раздувают API класса, внося порой то, чего там быть не должно (SRP). Вот тот же SoftDeletingTrait: ну, окей, вероятно это удобно. На первый взгляд. А с другой стороны, API раздут ненужным мне хламом: если удалили, то удалили, идите в задницу с удалёнными сущностями. С точки зрения бизнес-задач мне скорее всего никогда не нужно работать с ними (по крайней мере, в пользовательской части сайта).

Если я захочу работать с уже удалёнными, то я могу сделать отдельный mapper, который не учитывает «deletedAt», возможно, просто с отдельным флагом типа
PHP:
class DoctrineUserRepository implements UserRepository {
    public function __construct(EntityManager $em, $withSoftDeleted = false) {
        // ...
    }
}
и уже этот инстанс можно будет юзать в той же админке. Хотя скорее тут правильнее будет сделать 2 разных EntityManager'а с разным набором фильтров.

Т.е. трейты — это такое вертикальное расширение возможностей класса. Но как бэ так не принято: новая ответственность — новый объект. Нужно больше разных классов с разделением ответственности, а не один большой god object.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Собственно так и случилось в java, когда им понадобились итераторы в существующие интерфейсы.
Это не такая страшная штука, потому что там нет свойств: разрешена лишь суперпозиция существующих методов интерфейса, как это фактически было сделано для итераторов. Т.е. это по сути не даёт внести что-то новое.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
вообще, monkeypatching в любой реализации - штука стремная, я разгребаю щас код на yii, где общий код вынесен в поведения - это писец котенку,
намного лучше для изменения когда дублирования кода избегают через классические паттерны вроде декорации, аггрегации и стратегии.

помню случай, когда трейт полезен, но это бывает раз в 5 лет.
ситуация была такая:
есть драйвер к базе, есть класс мапинга на таблицу со структурой полей, где-то должны быть данные результата запроса, и где-то - методы для работы с базой через драйвер.
чтобы это все сложить, чтобы в объектах с данными не дублировалась работа с базой и структура таблиц, как в yii 1, можно подключать это трейтами.
через декорацию тоже можно, но чуть больше кода, служебных объектов
 

fixxxer

К.О.
Партнер клуба
Трейты - это все же не monkey patching, это compile-time copy-paste. Дженерики для бедных.

В Java недавно появилась такая вещь, как реализация по умолчанию в интерфейсах:

http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

В 80% случаев, когда я используют трейты, у меня как раз трейты являются такой реализацией по умолчанию (и идут в паре с интерфейсом). Остальные 20% - это когда хочется обойтись множественным наследованием, потому что лень по мелочи делать декомпозицию.
 

Вурдалак

Продвинутый новичок
В 80% случаев, когда я используют трейты, у меня как раз трейты являются такой реализацией по умолчанию
Это как?

Default-метод предполагает типа
Код:
public interface Foo {
    int getFoo();
    int getBar();

    default int getFooBar() {
        return getFoo() + getBar();
    }
}
В PHP:
PHP:
interface Foo {
    public function getFoo();
    public function getBar();
    public function getFooBar();
}

trait FooTrait { // "implements Foo" is not allowed
    public function getFooBar() {
        // woot, woot?
        return $this->getFoo() + $this->getBar();
    }
}
— откуда трейт узнает про getFoo(), getBar()?

Т.е. оно будет работать, но IDE будет намекать.
 

Вурдалак

Продвинутый новичок
Окей, а можно конкретный пример? Почему нельзя было сделать абстрактный класс? Такое ощущение, что тут нарушение моего любимого SRP.

Default-методы скорее всего появились, чтобы сохранить BC. Но в случае PHP с трейтами смысл теряется.
 
Сверху