Подтвердить что ты не робот

Разница между @Delegate, @Mixin и чертами в Groovy?

Кто-нибудь объяснит, когда я захочу использовать Groovy Traits против Mixins (@Mixin) против делегатов (@Delegate)? Возможно, некоторые компромиссы и проблемы дизайна помогут.

Все они, похоже, допускают повторное использование нескольких "классов" поведения. Благодарю.: -)

Этот поток SO был полезен также: Разница между преобразованиями @Delegate и @Mixin AST в Groovy

4b9b3361

Ответ 1

Я согласен, все они, похоже, позволяют повторно использовать несколько "классов" поведения. Однако существуют различия, и понимание этого, вероятно, поможет вашему решению.

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

Заключение/типичное использование:

  • @Delegate. Используется для добавления всех функциональных возможностей класса делегатов, но при этом избегать плотной связи с фактическая реализация. Позвольте вам достичь композиции над наследованием.
  • @Mixin: устарел с помощью groovy 2.3. Простой способ добавления методов из одного или нескольких классов в ваш класс. Ошибка охваченная.
  • Смешивание времени выполнения. Добавьте один или несколько методов в любой существующий класс, например. класс в JDK или сторонней библиотеке.
  • Черты: новое в groovy 2.3. Четко определенный способ добавления одного или нескольких признаков в ваш класс. Заменяет @Mixin. Единственный один из них, где добавленные методы видны в Java-классах.

И теперь давайте рассмотрим каждую из них немного подробнее.

@Delegate

Наследование используется во многих случаях. То есть, это часто неправильно используется. Классические примеры в Java: расширение входных потоков, считывателей или классов коллекции. Для большинства из них использование наследования слишком тесно связан с реализацией. То есть фактическая реализация написана так, что одна из общественные методы фактически используют другой. Если вы переопределите оба, и вы вызываете super, тогда вы можете получить нежелательные побочные эффекты. Если реализация изменится в более поздней версии, вам придется обновить свою обработку это также.

Вместо этого вы должны стремиться использовать композицию над наследованием.

Пример, список подсчета, который подсчитывает элементы, добавленные в список:

class CountingList<E> {
    int counter = 0
    @Delegate LinkedList<E> list = new LinkedList<>()
    boolean addAll(Collection<? extends E> c) {
        counter += c.size()
        list.addAll(c)
    }
    boolean addAll(int index, Collection<? extends E> c) {
        counter += c.size()
        list.addAll(index, c)
    }
    // more add methods with counter updates
}

В этом примере @Delegate удаляет весь утомительный код котельной для всех общедоступных методов, которые вы хотите оставить "as-is", т.е. добавлены методы, которые просто переадресуют вызов в базовый список. К тому же, CountingList отделен от реализации, так что вам не нужно заботиться о том, является ли один из этих методы реализуются путем вызова другого. В приведенном выше примере это действительно так, поскольку LinkedList.add(Collection) вызывает LinkedList.add(int, Collection), так что это не так прямолинейно для реализации с использованием наследования.

Резюме:

  • Предоставляет стандартные реализации для всех общедоступных методов в делегированном объекте.
    • Методы с одинаковой подписью, которые явно добавлены, имеют приоритет.
  • Неявно добавленные методы не видны в Java.
  • Вы можете добавить несколько @Delegate в один класс.
  • Класс с делегатами (CountingList в приведенном выше примере) не являются экземплярами класса делегата.
    • т.е. CountingList не является экземпляром LinkedList.
  • Используйте, чтобы избежать плотной связи через наследование.

@Mixin

Преобразование @Mixin будет устаревать с помощью groovy 2.3, из-за поддержки предстоящих признаков. Это обеспечивает что все, что возможно сделать с @Mixin, должно быть возможно сделать с чертами вместо этого.

По моему опыту, @Mixin является своего рода смешанным благословением.:)

Это, по мнению разработчиков основных разработчиков, связано с ошибками "трудно решаемых". Это не значит, что это было "бесполезный", далекий от него. Но если у вас есть возможность использовать (или ждать) groovy 2.3, то вы должны использовать вместо этого.

Что делает трансформация AST, просто добавить методы из одного класса в другой. Например:

class First {
    String hello(String name) { "Hello $name!" }
}

@Mixin(First)
class Second {
    // more methods
}

assert new Second().hello('Vahid') == 'Hello Vahid!'

Резюме:

  • Добавляет методы из одного класса в другой.
  • Использовать в groovy < 2.3 для простого добавления методов из одного класса в другой
    • не добавляйте к классам "супер" (по крайней мере, у меня были проблемы с этим)
  • Исправлена ​​ошибка охваченного
  • Устаревший из groovy 2.3
  • Неявно добавленные методы не видны в Java.
  • Класс, который получает другой класс, смешанный, не является экземплярами этого другого класса
    • т.е. Second не является экземпляром First
  • Вы можете смешивать несколько классов в один класс
  • Использовать как простой способ добавления функциональности одного класса в другой в groovy < 2.3

Микширование времени выполнения

Микширование времени выполнения и преобразование @Mixin совершенно разные, они решают разные прецеденты и используются в совершенно разных ситуациях. Поскольку у них одно и то же имя, легко смутить одно с другим или думайте, что они одно и то же. Однако смешения в среде исполнения не устарели в groovy 2.3.

Я склонен думать о runtime mixins как способе добавления методов к существующим классам, таким как любой класс в JDK. Это механизм, используемый groovy для добавления дополнительных методов в JDK.

Пример:

class MyStringExtension {
    public static String hello(String self) {
        return "Hello $self!"
    }
}

String.mixin(MyStringExtension)

assert "Vahid".hello() == 'Hello Vahid!'

Groovy также имеет приятный модуль расширения, где вам не нужно вручную выполнять микширование, вместо этого groovy делает это для вас до тех пор, пока находит дескриптор модуля в правильном месте в пути к классам.

Резюме:

  • Добавить методы в любой существующий класс
    • любые классы в JDK
    • любые классы сторонних разработчиков
    • или любой из ваших собственных классов
  • Переопределяет любой существующий метод с той же сигнатурой
  • Добавленные методы не видны в Java
  • Обычно используется для расширения существующих/сторонних классов с новыми функциями

Черты характера

Черты новы для groovy 2.3.

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

Черты похожи на преобразование @Mixin, которое они заменяют, но они также более мощные. Во-первых, они гораздо более четко определены. Невозможно создать экземпляр напрямую, как интерфейс, им нужна реализация класс. И класс может реализовать многие черты.

Простой пример:

trait Name {
    abstract String name()
    String myNameIs() { "My name is ${name()}!" }
}
trait Age {
    int age() { 42 }
}

class Person implements Name, Age {
    String name() { 'Vahid' }
}

def p = new Person()
assert p.myNameIs() == 'My name is Vahid!'
assert p.age() == 42
assert p instanceof Name
assert p instanceof Age

Непосредственная разница между признаками и @Mixin заключается в том, что trait является ключевым словом языка, а не преобразованием AST. Кроме того, он может содержать абстрактные методы, которые должны выполняться классом. Кроме того, класс может реализовать несколько черт. Класс, реализующий признак, является экземпляром этого признака.

Резюме:

  • Черты предоставляют интерфейс с реализацией и состоянием.
  • Класс может реализовать несколько признаков.
  • Методы, реализуемые признаком, видны в Java.
  • Совместимость с проверкой типов и статической компиляцией.
  • Черты могут реализовывать интерфейсы.
  • Черты не могут быть созданы сами по себе.
  • Черта может расширять еще один признак.
  • Решение проблемы четко определено.
  • Типичное использование:
    • добавить похожие черты в разные классы.
      • (как альтернатива АОП)
    • создать новый класс из нескольких признаков.