Categorias em Groovy
1. Visão geral
Às vezes, podemos nos perguntar se podemos adicionar alguns métodos úteis adicionais para compilar classes Java ou Groovy onde não temos a capacidade de modificar o código-fonte. Como se vê, uma categoria Groovy nos permite fazer exatamente isso.
Groovy é uma linguagem JVM dinâmica e poderosa que possui vários recursosMetaprogramming.
Neste tutorial, vamos explorar o conceito de categorias no Groovy.
2. O que é uma categoria?
As categorias são um recurso de metaprogramação, inspirado no Objective-C, que permite adicionar funcionalidades extras a uma classe Java ou Groovy nova ou existente.
Ao contrário deextensions, os recursos adicionais fornecidos por uma categoria não são habilitados por padrão. Portanto, a chave para habilitar uma categoria é o bloco de códigouse.
Os recursos extras implementados por uma categoria são acessíveis apenas dentro do bloco de códigouse.
3. Categorias em Groovy
Vamos discutir algumas categorias proeminentes que já estão disponíveis no Groovy Development Kit.
3.1. TimeCategory
A classeTimeCategory está disponível no pacotegroovy.time que adiciona algumas maneiras úteis de trabalhar com objetosDateeTime.
Esta categoria adiciona a capacidade aconvert an Integer into a time notation like seconds, minutes, days, and months.
Além disso, a classeTimeCategory fornece métodos comopluseminus paraadding Duration to Date objects and subtracting Duration from Date objects facilmente, respectivamente.
Vamos examinar alguns recursos úteis fornecidos pela classeTimeCategory. Para esses exemplos, primeiro criaremos um objetoDate e, em seguida, realizaremos algumas operações usandoTimeCategory:
def jan_1_2019 = new Date("01/01/2019")
use (TimeCategory) {
assert jan_1_2019 + 10.seconds == new Date("01/01/2019 00:00:10")
assert jan_1_2019 + 20.minutes == new Date("01/01/2019 00:20:00")
assert jan_1_2019 - 1.day == new Date("12/31/2018")
assert jan_1_2019 - 2.months == new Date("11/01/2018")
}
Vamos discutir o código em detalhes.
Aqui,10.seconds cria o objetoTimeDuration com o valor de 10 segundos. E, o operador de mais (+) adiciona o objetoTimeDuration ao objetoDate.
Da mesma forma,1.day cria o objetoDuration com o valor de 1 dia. E, o operador menos (-) subtrai o objetoDuration do objetoDate.
Além disso, alguns métodos comonow,ago,efrom estão disponíveis por meio da classeTimeCategory, que permitecreating relative dates.
Por exemplo,5.days.from.now criará um objetoDate com o valor de 5 dias antes da data atual. Da mesma forma,2.hours.ago define o valor de 2 horas antes da hora atual.
Vamos olhar para eles em ação. Além disso, usaremosSimpleDateFormat para ignorar os limites de tempo ao comparar dois objetosDate semelhantes:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy")
use (TimeCategory) {
assert sdf.format(5.days.from.now) == sdf.format(new Date() + 5.days)
sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss")
assert sdf.format(10.minutes.from.now) == sdf.format(new Date() + 10.minutes)
assert sdf.format(2.hours.ago) == sdf.format(new Date() - 2.hours)
}
Portanto, usando a classeTimeCategory, podemos escrever um código simples e mais legível usando as classes que já conhecemos.
3.2. DOMCategory
A classeDOMCategory está disponível no pacotegroovy.xml.dom. Ele oferece algumas maneiras úteis de trabalhar com o objeto DOM do Java.
Mais especificamente,DOMCategory allows GPath operations on DOM elements, allowing easier traversal and processing of XMLs.
Primeiro, vamos escrever um texto XML simples e analisá-lo usando a classeDOMBuilder:
def exampleArticlesText = """
An Intro to the Java Debug Interface (JDI)
A quick and practical overview of Java Debug Interface.
A Quick Guide to Working with Web Services in Groovy
Learn how to work with Web Services in Groovy.
"""
def exampleArticlesDom = DOMBuilder.newInstance().parseText(exampleArticlesText)
def root = exampleArticlesDom.documentElement
Aqui, o objetoroot contém todos os nós-filhos do DOM. Vamos percorrer esses nós usando a classeDOMCategory:
use (DOMCategory) {
assert root.article.size() == 2
def articles = root.article
assert articles[0].title.text() == "An Intro to the Java Debug Interface (JDI)"
assert articles[1].desc.text() == "Learn how to work with Web Services in Groovy."
}
Aqui, a classeDOMCategory permiteeasy access of nodes and elements using dot operations fornecidos porGPath. Além disso, ele fornece métodos comosize and text to access the information of any node or element.
Agora, vamos acrescentar um novo nó ao objeto DOMroot usandoDOMCategory:
use (DOMCategory) {
def articleNode3 = root.appendNode(new QName("article"), ["core-java": "false"])
articleNode3.appendNode("title", "Metaprogramming in Groovy")
articleNode3.appendNode("desc", "Explore the concept of metaprogramming in Groovy")
assert root.article.size() == 3
assert root.article[2].title.text() == "Metaprogramming in Groovy"
}
Da mesma forma, a classeDOMCategory também contém alguns métodos comoappendNode and setValue to modify the DOM.
4. Crie uma categoria
Agora que vimos algumas categorias do Groovy em ação, vamos explorar como criar uma categoria personalizada.
4.1. Usando Auto Objeto
Uma classe de categoria deve seguir certas práticas para implementar recursos adicionais.
Primeiro, o método que adiciona um recurso adicional deve serstatic. Em segundo lugar, o primeiro argumento do método deve ser o objeto ao qual esse novo recurso é aplicável.
Vamos adicionar o recursocapitalize à classeString. Isso simplesmente mudará a primeira letra deString para maiúscula.
Primeiramente, vamos escrever a classeexampleCategory com um métodostaticcapitalizee tipoString como seu primeiro argumento:
class exampleCategory {
public static String capitalize(String self) {
String capitalizedStr = self;
if (self.size() > 0) {
capitalizedStr = self.substring(0, 1).toUpperCase() + self.substring(1);
}
return capitalizedStr
}
}
A seguir, vamos escrever um teste rápido para habilitarexampleCategorye verificar o recursocapitalize no objetoString:
use (exampleCategory) {
assert "norman".capitalize() == "Norman"
}
Da mesma forma, vamos escrever um recurso para elevar um número à potência de outro número:
public static double toThePower(Number self, Number exponent) {
return Math.pow(self, exponent);
}
Por fim, vamos testar nossa categoria personalizada:
use (exampleCategory) {
assert 50.toThePower(2) == 2500
assert 2.4.toThePower(4) == 33.1776
}
4.2. @Category Anotação
Também podemos usar a anotação@groovy.lang.Category paradeclare a category as an instance-style class. Ao usar a anotação, devemos fornecer o nome da classe à qual nossa categoria é aplicável.
A instância do objeto é acessível usando a palavra-chavethis em um método. Portanto, o objetoself não precisa ser o primeiro argumento.
Vamos escrever uma classeNumberCategory e declará-la como uma categoria com a anotação@Category. Além disso, adicionaremos alguns recursos adicionais comocubeedivideWithRoundUp à nossa nova categoria:
@Category(Number)
class NumberCategory {
public Number cube() {
return this*this*this
}
public int divideWithRoundUp(BigDecimal divisor, boolean isRoundUp) {
def mathRound = isRoundUp ? BigDecimal.ROUND_UP : BigDecimal.ROUND_DOWN
return (int)new BigDecimal(this).divide(divisor, 0, mathRound)
}
}
Aqui, o recursodivideWithRoundUp divide um número pelo divisor e arredonda para cima / para baixo o resultado para o próximo inteiro ou anterior com base no parâmetroisRoundUp.
Vamos testar nossa nova categoria:
use (NumberCategory) {
assert 3.cube() == 27
assert 25.divideWithRoundUp(6, true) == 5
assert 120.23.divideWithRoundUp(6.1, true) == 20
assert 150.9.divideWithRoundUp(12.1, false) == 12
}
5. Conclusão
Neste artigo, exploramos o conceito de categorias no Groovy - um recurso de metaprogramação que pode ativar recursos adicionais nas classes Java e Groovy.
Examinamos algumas categorias comoTimeCategoryeDOMCategory, que já estão disponíveis no Groovy. Ao mesmo tempo, exploramos algumas maneiras úteis adicionais de trabalhar comDatee DOM do Java usando essas categorias.
Por último, exploramos algumas maneiras de criar nossa própria categoria personalizada.
Como de costume, todas as implementações de código estão disponíveisover on GitHub.