Наша совместная команда Banwar.org

Связаться с нами

  • (097) ?601-88-87
    (067) ?493-44-27
    (096) ?830-00-01

Статьи

Groovy і Gradle - помічники Java

У київському офісі DataArt пройшов тренінг «Groovy і Gradle для Java-розробників». Все було чудово організовано, і нам захотілося поділитися враженнями.
В першу чергу, спасибі прекрасному тренеру Євгену Борисову, який на два дні повністю відсунув всі наші життєві питання і проблеми. Тренінг був суперський, позитивний, динамічний і дуже захоплюючий.

Євген почав виступ з невеликого екскурсу в історію появи мови Groovy, що почалася в далекому 2003 році. Чому цей лаконічний і потужна мова розробки до сих пір не зайняв лідируючу позицію - окрема, трохи сумна історія, пов'язана, крім іншого, ще й з підтримкою IDE.

У перший день тренінгу аудиторія осягала Groovy, не всі були з ним знайомі, та й багато хто з тих, хто вже був знайомий, дізналися купу всіляких цікавих особливостей мови. Зокрема, розглянули взаємодію Groovy і Java, встановили досить цікавий факт - відсутність суперництва між цими потужними мовами, вони, скоріше, доповнюють один одного.

Багатьох Java-розробників може насторожити черговий JVM-мову - мовляв, «я і так вже знаю один і він мене влаштовує, навіщо мені переходити на інший? А як же все ті фреймворки, ліби, якими я навчився користуватися? Як же мій дорогоцінний досвід? ». Але Groovy прийшов не витіснити Java - він прийшов їй допомогти. Згадайте, скільки потрібно написати Java-коду, щоб прочитати рядки з текстового файлу і вивести їх на екран, прочитати XML- або JSON-файл і спарсіровать їх вміст в об'єктний граф. Скільки потрібно try- і catch-блоків в яких, дуже часто не зовсім зрозуміло, що потрібно робити і, в кращому випадку, ексепшн логіруется, а в гіршому - просто ігнорується (і потім спробуй знайди, де помилка сталася). Скільки потрібно POJO написати, щоб промепіть на них спарсірованние дані з JSON і XML? А в Groovy все простіше: checked exceptions не підтримуються - немає необхідності писати код, який буде їх обробляти. І є спеціальні класи, які дають можливість в парі рядків коду отримати об'єктний граф з запитаного веб сервісу або файлу в форматі JSON / XML. І ми наведемо приклад, як це робиться.

Далі ми розглянули варіанти запуску програм на Groovy. Потім перейшли до лексичним особливостям мови - closure, більш повною версією лямбда-виразів, що з'явилися в Java 1.8.

До речі, в зв'язку з тим, що в Groovy правлять бал closures, необхідність у внутрішніх класах автоматично відпала, і вони просто не підтримуються. Closure - свого роду покажчик на метод. Наприклад, цей код всередині Groovy скрипта виведе "hello" на консоль:

def clos = {println "hello!" } Clos () // надрукує "hello!"

Як бачимо, код не повинен обов'язково бути всередині класу, точки з запитом в кінці стейтменті необов'язкові, дужки в кінці методу опційні, якщо він приймає хоча б один параметр, тип змінної вказувати не обов'язково (ключове слово def - якщо не вказано, правда, до цієї можливості варто вдаватися, коли дійсно тип наперед невідомий). А ось так closure буде виглядати з параметрами:

def printSum = {a, b -> println a + b} printSum (5, 7) // надрукує "12"

Як бачимо, параметри в closure вказуються перед ->, а якщо -> відсутня, closure отримує один неявний параметр "it". Ось приклад його використання:

def stringList = [ "java", "perl", "python", "ruby", "c #", "cobol", "groovy"]; stringList.each () {print "$ {it}"}; // виведе: java perl python ruby ​​c # cobol groovy

І все - це весь скриптова файл! Тут у нас квадратними скобочки створюється ArrayList і Groovy дає нам можливість працювати з ним, як з масивом. Вірніше, як з крутим масивом з купою різних корисних перевантажених операторів і методів доданих до класу за допомогою GDK (Groovy Development Kit). Загалом, Groovy дає можливість сидіти на двох стільцях одночасно - користуватися багатим лаконічним синтаксисом, використовуючи списки. Ніяких імпорт пакета, який вже став всюдисущим в Java - java.util. * (Автоматично в Groovy теж імпортуються: groovy.lang. *

groovy.util. * java.lang. * java.util. * java.net. * java.io. * java.math.BigInteger java.math.BigDecimal). Метод each () пробігається по кожному елементу списку і робить з ним ту дію, яку вказано в closure. В даному випадку виводитися значення елемента. Для цього використовується чудова Groovy рядок GString (починається і закінчується подвійними лапками, звичайна рядок починається і закінчується одинарними лапками.) Всякий раз, коли в GString зустрічається $ {...}, вираз всередині обчислюється і перетворюється в рядок. Дуже компактно і зручно.
А ось приклад роботи з картою:

def stringMap = [ "Su": "Sunday", "Mo": "Monday", "Tu": "Tuesday", "We": "Wednesday", "Th": "Thursday"]; stringMap.each () {key, value -> println "$ {key} == $ {value}"}; // Su == Sunday // We == Wednesday // Mo == Monday // Th == Thursday // Tu == Tuesday

Тут у нас створюється карта (за замовчуванням HashMap) і відразу ж заповнюється записами (в кожного запису - ключі до двокрапки і значення після двокрапки), а метод each () пробігається по кожного запису карти і робить з нею, що зазначено в переданому closure. Зокрема, виводить на консоль рядок виду "ключ == значення". Ще одна з численних відмінних речей в Groovy - оператор безпечної навігації. Він дає можливість позбутися від обтяжливого, рутинного коду який необхідний в Java, щоб не натрапити на NPE. Ось він в дії:

def user = User.find ( 'admin') // тут user може виявитися null якщо 'admin' не існує def streetName = user? .address? .street // а тут streetName стане null якщо user або user.address виявилися null. І ніякого NPE)

Ми розглянули роботу з text-, XML- і JSON-файлами, пощупали колекції, оцінили зручність і потужність GString (тільки не треба гуглити картинки. Результат пошуку раптовий і не відповідає). Краєм ока подивилися на різні анотації (наприклад, прекрасна анотація @Canonical - до анотований класу автоматично створюються геттери / сеттери, генерується toString (), всілякі конструктори, hashcode і equals - в загальному, весь той базовий набір, який ми використовуємо майже кожен раз при створенні нового класу).
Пильно подивилися і спробували всілякі цікаві штуки, типу «а не додати нам до бібліотечного класу свій метод?» - навіщо, скажіть будь ласка, обмежувати себе класом? Можна ж підлаштовувати і один конкретний об'єкт цього класу!

Хоч і написано в дусі «подивилися - побачили», насправді краще читати це як «спробували». Тому що за допомогою Groovy робити це було просто, тим більше, з докладними коментарями тренера.

Мабуть, найбільш примітним прикладом буде задачка, яку ми робили: є клас Money, з полями amount і currency (сума і валюта - відповідно). Необхідно написати перевизначити один метод класу так, щоб при звичайному математичному складення двох об'єктів класу результатом був третій об'єкт цього ж класу, де буде загальна сума коштів і валюта першого доданка. З конвертацією за поточним курсом (витягає як властивість JSON з певного URL). Задумайтесь, скільки рядків коду це займе на Java? Скільки всього треба буде написати? Скільки бібліотек підключити? А на Groovy це все зайняло цілих ... 7. Ну ладно, гарненько відформатованих - вісім. Правда, показово? Давайте подивимося на код:

@ToString class Money {final BigDecimal amount final String currency Money (BigDecimal amount, String currency) {this.amount = amount this.currency = currency} Money plus (Money money) {if (money.currency == this.currency) { new Money (amount + money.amount, this.currency)} else {def rate = new JsonSlurper (). parse (new URL ( " http://rate-exchange.appspot.com/currency?from=${money.currency}&to=${this.currency} ")). Rate new Money (amount + (money.amount * rate as BigDecimal), this.currency)}}} def bucks = new Money (10, 'USD') def eurs = new Money (4, 'EUR' ) println "dollars = $ {bucks + eurs}" // у нас вийшло 15.44 доларів)

Як видно, основна частина коду, яка відповідає за бізнес-логіку, міститься в методі plus (). Його перевизначення перевантажує оператор + (аналогічним чином в Groovy перевантажуються і інші оператори). Якщо валюта другого доданка Money відрізняється від валюти першого, вона конвертується в валюту першого. Метод parse JsonSlurper парсірует отриманий від веб-сервісу відповідь в ледачу карту (LazyMap), де можемо достукатися до значень її записів за назвами їх ключів (в нашому випадку це rate), як ніби є такі атрибути. Загалом, кредо Groovy - «геть церемоніальність».

Так що частина тренінгу, присвячена Groovy, залишила просто незабутнє враження. Але на цьому справа не закінчилася, і на другий день ми, допілівая практичні завдання і озброївшись прекрасним кавою / чаєм, почали активно вгризатися в твердий граніт Gradle!

Для початку уточнили різницю між декларативним інструментом побудови та імперативним. Ну, будемо говорити відверто: всім нам приємніше сказати, ЩО ми хочемо, і отримати результат, ніж докладно пояснювати, ЯК домогтися потрібного нам результату. Чудовим прикладом будуть слова Євгена: «зроби мені чай» - декларативний підхід, «відкрий ці двері, пройди по коридору, візьми стаканчик, постав чайник, візьми пакетик чаю, який лежить ... і т. Д.» - імперативний. Звичайно, ніхто в групі не взявся спростовувати, що іноді, коли нам необхідно щось більш унікальне, сильніше відповідає нашим очікуванням, другий підхід повністю себе виправдовує. Але в більшості випадків багато речей - звичайна рутина, і пояснювати в 100500-й раз, що я просто хочу чай, - утомливо. І тут уважний читач скаже: «Ну так є ж Maven! Він же підходить і все робить так, як треба! »І буде в чомусь абсолютно прав, крім двох малесеньких і непомітних речей: XML - не найзручніший мову для написання скрипта. <Build> ... </ build> -секція - така інтуїтивно зрозуміла і проста, що іноді плакати хочеться.

Чим же так виділяється Gradle? Так, в общем-то, всім ... Для початку, скрипти, написані на Gradle, по суті, Groovy. Але з можливістю складання, найрізноманітнішими плагінами і іншими подібними вкусняшками. Основна одиниця в Gradle-скрипті - task, який може бути легко розширений за допомогою наслідування, створення свого підкласу, взаємозалежності tasks.

Правда, перше враження було злегка двояким: найпростіший task, все, що робить, - виводить час. Збирається близько семи секунд. ЩО ?! Сім секунд, щоб час вивести ?! Це була перша реакція на роботу збирача. Насправді, Євген дуже швидко пояснив причину такої «швидкої» роботи - кожен раз при запуску скрипта запускалася віртуальна машина, на якій він виконувався. А потім закривалася. Завдяки використанню такої корисної фішки, як Gradle daemon, яка описується в файлі налаштувань для проекту. До речі, робота з файлом налаштувань (gradle.properties) - суцільне задоволення, звернення до нього просте (Groovy ж!), Проблем-ніяких, ну а настройки - завжди корисні.

Після чого ми більш детально подивилися, як влаштовані tasks всередині, що можна в них передати, як користуватися залежностями і визначати настройки всередині самого task - наприклад, при використанні методу onlyif можна налаштувати розклад виконання. Так само пройшлися по темі створення власних плагінів для Gradle-скрипта. Хорошійпоказатель - плагін, який при невеликому шаманизме може відправляти e-mail, наприклад, якщо білд не виконав по якимось причинам. Розглянули варіант, як з ланцюжка tasks можна видалити один або цілий ланцюжок. Ну, і куди ж без цього - пощупали можливість усунення конфліктів залежностей.

Мабуть, найцікавіше і приємне, що було в другій день тренінгу, - приклад налаштування сумісності з такими популярними, але не завжди зручними збирачами, як Maven і Ant. Сумісність не просто є, а прекрасно підтримується.

Підводячи підсумки, можна не сказати, що все пройшло просто чудово. Євген постійно заряджав ентузіазмом (особливо це було потрібно вранці), аудиторія активно обговорювала те, що відбувається, і судячи з облич і спілкуванню, задоволення отримали всі. Але головне, що всі отримали найцінніший досвід по роботі з двома прекрасними інструментами - мовою програмування і збирачем. У багатьох з'явилися думки, що можливо, щось ми в Java втрачаємо. Навіть не так ... не використовуємо на 100% весь потенціал.

Багатьох Java-розробників може насторожити черговий JVM-мову - мовляв, «я і так вже знаю один і він мене влаштовує, навіщо мені переходити на інший?
А як же все ті фреймворки, ліби, якими я навчився користуватися?
Як же мій дорогоцінний досвід?
Скільки потрібно POJO написати, щоб промепіть на них спарсірованние дані з JSON і XML?
Find ( 'admin') // тут user може виявитися null якщо 'admin' не існує def streetName = user?
Address?
Пильно подивилися і спробували всілякі цікаві штуки, типу «а не додати нам до бібліотечного класу свій метод?
» - навіщо, скажіть будь ласка, обмежувати себе класом?
Задумайтесь, скільки рядків коду це займе на Java?
Скільки всього треба буде написати?

Новости

Banwar.org
Наша совместная команда Banwar.org. Сайт казино "Пари Матч" теперь доступен для всех желающих, жаждущих волнения и азартных приключений.