Ответ 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.
- Совместимость с проверкой типов и статической компиляцией.
- Черты могут реализовывать интерфейсы.
- Черты не могут быть созданы сами по себе.
- Черта может расширять еще один признак.
- Решение проблемы четко определено.
- Типичное использование:
- добавить похожие черты в разные классы.
- (как альтернатива АОП)
- создать новый класс из нескольких признаков.
- добавить похожие черты в разные классы.