Groovy 2.0 принес нам модули расширения. Модуль расширения представляет собой файл JAR с классами, которые предоставляют дополнительные методы для существующих других классов, таких как JDK или сторонние библиотеки. Groovy использует этот механизм для добавления, например, дополнительных методов в File
класс. Мы можем реализовать наш собственный модуль расширения для добавления новых методов расширения в существующие классы. После того, как мы написали модуль, мы можем добавить его в путь к классам нашего кода или приложения, и все новые методы будут сразу же доступны.
Мы определяем новые методы расширения в вспомогательных классах, которые являются частью модуля. Мы можем создавать экземпляры и статические методы расширения, но нам нужен отдельный вспомогательный класс для каждого типа метода расширения. Мы не можем смешивать статические и методы расширения экземпляра в одном вспомогательном классе. Сначала мы создаем очень простой класс с методом расширения для String
класса. Первый аргумент метода расширения определяет тип или класс, к которому мы хотим добавить метод. Следующий код показывает метод likeAPirate
. Метод расширения должен быть, public
и static
хотя мы создаем метод расширения экземпляра.
// File: src/main/groovy/com/mrhaki/groovy/PirateExtension.groovy package com.mrhaki.groovy class PirateExtension { static String likeAPirate(final String self) { // List of pirate language translations. def translations = [ ["hello", "ahoy"], ["Hi", "Yo-ho-ho"], ['are', 'be'], ['am', 'be'], ['is', 'be'], ['the', "th'"], ['you', 'ye'], ['your', 'yer'], ['of', "o'"] ] // Translate the original String to a // pirate language String. String result = self translations.each { translate -> result = result.replaceAll(translate[0], translate[1]) } result } }
Далее нам нужно создать файл дескриптора модуля расширения. В этом файле мы определяем имя вспомогательного класса, поэтому Groovy будет знать, как его использовать. Файл дескриптора должен быть размещен в META-INF/services
каталоге нашего архива модуля или пути к классам. Имя файла есть org.codehaus.groovy.runtime.ExtensionModule
. В файле мы определяем имя нашего модуля, версию и имя вспомогательного класса. Имя вспомогательного класса определяется с помощью свойства extensionClasses
:
# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = pirate-module moduleVersion = 1.0 extensionClasses = com.mrhaki.groovy.PirateExtension
Теперь модуль расширения готов. Самый простой способ распространения модуля — это упаковать файл кода и дескриптора в файл JAR и поместить его в менеджер репозитория артефактов. Затем другие разработчики могут использовать инструменты сборки, такие как Gradle или Maven, чтобы включить модуль расширения в свои проекты и приложения. Если мы используем Gradle для создания JAR-файла, нам нужен только этот небольшой скрипт сборки:
/ File: build.gradle apply plugin: 'groovy' repositories.mavenCentral() dependencies { // Since Gradle 1.4 we don't use the groovy configuration // to define dependencies. We can simply use the // compile and testCompile configurations. compile 'org.codehaus.groovy:groovy-all:2.0.6' }
Теперь мы можем вызвать $ gradle build
и получили модуль расширения.
Давайте добавим тест для нашего нового метода расширения. Поскольку мы используем Gradle, тестовый путь к классам уже будет содержать вспомогательный класс нашего модуля расширения и файл дескриптора. В нашем тесте мы можем просто вызвать метод и проверить результаты. Мы собираемся использовать Спока, чтобы написать простую спецификацию:
// File: src/test/groovy/com/mrhaki/groovy/PirateExtensionSpec.groovy package com.mrhaki.groovy import spock.lang.Specification class PirateExtensionSpec extends Specification { def "likeAPirate method should work as instance method on a String value"() { given: final String originalText = "Hi, Groovy is the greatest language of the JVM." expect: originalText.likeAPirate() == "Yo-ho-ho, Groovy be th' greatest language o' th' JVM." } }
Мы добавляем зависимость к Споку в нашем файле сборки Gradle:
/ File: build.gradle apply plugin: 'groovy' repositories.mavenCentral() dependencies { // Since Gradle 1.4 we don't use the groovy configuration // to define dependencies. We can simply use the // compile and testCompile configurations. compile 'org.codehaus.groovy:groovy-all:2.0.6' testCompile 'org.spockframework:spock-core:0.7-groovy-2.0' }
Мы можем запустить $ gradle test
для запуска спецификации Spock и протестировать наш новый метод расширения.
Чтобы добавить статический метод в существующий класс, нам нужно добавить дополнительный вспомогательный класс в наш модуль расширения и дополнительное свойство в наш файл дескриптора для регистрации вспомогательного класса. Первый аргумент метода расширения определяет тип, к которому мы хотим добавить статический метод. В следующем вспомогательном классе мы добавляем talkLikeAPirate()
в String
класс метод расширения .
/ File: src/main/groovy/com/mrhaki/groovy/PirateStaticExtension.groovy package com.mrhaki.groovy class PirateStaticExtension { static String talkLikeAPirate(final String type) { "Arr, me hearty," }
Мы изменим файл дескриптора и добавим staticExtensionClasses
свойство:
# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule moduleName = pirate-module moduleVersion = 1.0 extensionClasses = com.mrhaki.groovy.PirateExtension staticExtensionClasses = com.mrhaki.groovy.PirateStaticExtension
В нашей спецификации Spock мы добавляем дополнительный тест для нашего нового статического метода talkLikeAPirate()
в String
классе:
// File: src/test/groovy/com/mrhaki/groovy/PirateExtensionSpec.groovy package com.mrhaki.groovy import spock.lang.Specification class PirateExtensionSpec extends Specification { def "likeAPirate method should work as instance method on a String value"() { given: final String originalText = "Hi, Groovy is the greatest language of the JVM." expect: originalText.likeAPirate() == "Yo-ho-ho, Groovy be th' greatest language o' th' JVM." } def "talkLikeAPirate method should work as static method on String class"() { expect: "Arr, me hearty, Groovy rocks!" == String.talkLikeAPirate() + " Groovy rocks!" } }
Написано с Groovy 2.1