Как стать автором
Обновить

Groovy inspiration — Feel the difference

Время на прочтение6 мин
Количество просмотров4.8K
image

Почему Groovy


Будучи Java разработчиком, некоторое время назад я начал посматривать в сторону других языков программирования, и, думаю об этом размышлял далеко не только я. Некоторые мои знакомые, в свое время имеющие отнюдь не малый опыт в разработке под Java — платформу, решительно начали двигаться по рельсам (Rails), соответственно используя Ruby, кто-то еще подумывает приручить Питона с приложением к нему в виде Django. Появляется достаточно книг о том, как Java — программисту мигрировать в мир динамических языков. Может ли что-то нас остановить?

Так сложилось, что не так давно мне довелось наблюдать разработку на Ruby&Rails с соседнего от напарника стула и даже самому кое-что пописать, временами выслушивая критику в сторону Java — style. В результате, пока точно не знаю, к сожалению или счастью, но меня не затянуло. Все-таки не хотелось оставлять, возможно, по определенным меркам не очень большой, но все же для меня основной — почти 3х летний опыт работы с Java. В то же время я могу легко наплевать на немалую часть времени, потраченного на изучение AS3/Flex, чьи компоненты были неотъемлемыми при написании приложений, использующих технологии потокового видео. Но, к счастью, с этой практикой вроде покончено.
В эти выходные мне на глаза попалась какая-то статья про Groovy, точно не могу вспомнить с чего все началось. После этого, посмотрев пару записанных конференций, в этот раз Groovy меня определенно заинтересовал. Язык разработан для платформы Java и является наиболее популярным из других, динамических языков, работающих в JVM. Порадовала, конечно, довольно простая интеграция с компонентами на привычном мне языке, возможность использования хорошо знакомых фреймворков, работа в OSGi среде — все то, что реально останавливает меня от ухода из мира Java, давая при этом возможность писать элегантный код.
Ну, хватит воды, а то ее получилось уже довольно много, лучше приступлю к делу и попытаюсь объяснить почему меня вдохновил Groovy, экскурсия по которому вошла в число основных моих занятий на прошедшие выходные.

JavaBeans и Groovy


Изучив немного Java и познав основы инкапсуляции, неважно в каком порядке эти два действия совершены, большинство из нас сталкивается с необходимостью написания JavaBeans — содания полей класса и генерации всеми “любимых” getters/setters. В IDE для этого, кажется, даже HotKey сделали. Ниже представлен JavaBean на Java и Groovy соответственно.
//Java
import java.util.Date;

public class Employee {
 private String firstName;
 private String lastName;
 private Date dateSince;

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public Date getDateSince() {
  return dateSince;
 }

 public void setDateSince(Date dateSince) {
  this.dateSince = dateSince;
 }
}

//Groovy
class Employee {
 String firstName
 String lastName
 Date dateSince
}

Получилось 31 строка на Java против 5ти на Groovy.
И это еще без дополнительных конструкторов с параметрами, написание в Groovy которых вообще не требуется:
def e = new Employee(firstName:'Ivan', lastName:'Petrov')

Скажете где private или то, что не всегда надо давать возможность менять состояние объектов (-seeters + constructor)? И такая проблема оказывается решаемой с аннотацией @Immutable
import groovy.lang.Immutable;

@Immutable class Employee {
 String firstName
 String lastName
 Date dateSince
}

Exceptions


Касательно этой темы можно спорить достаточно долго… Как известно в Java исключения делятся на checked (проверяемые) и unchecked (непроверяемые). В Groovy же все исключения можно отнести к классу unchecked, то есть вас никто не заставит отлавливать их при вызове методов или указывать, что обработка будет где-то выше. Тут можно подумать о том, что все-таки мы не дети и надзиратель для этого нам не нужен. Но, в то же время, никто не мешает указывать throws или оборачивать код в try/catch блоки.

Делегирование


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

import java.text.SimpleDateFormat

class Event {
 @Delegate Date when
 String title, url
}

def df = new SimpleDateFormat("yyyy/MM/dd")

def gr8conf = new Event(title: "GR8 Conference",
url: "http://www.gr8conf.org",
when: df.parse("2009/05/18"))
def javaOne = new Event(title: "JavaOne",
url: "http://java.sun.com/javaone/",
when: df.parse("2009/06/02"))

assert gr8conf.before(javaOne.when)


Как видно, теперь можно без проблем использовать методы класса Date. Выражение gr8conf.before(javaOne.when) кажется вполне логичным, сохраняя при этом чистоту кода.
Пример, конечно, очень примитивный, но, думаю каждый сможет у себя в голове воспроизвести те моменты, в которых бы это ему могло помочь.

Замыкания (Closures)


В Java замыканий нет, и точно нельзя сказать когда они появятся, в Java 8 как планируется или еще раз перенесутся. В Groovy же их можно использовать уже сейчас. Я не буду тут очень детально рассматривать замыкания, думаю, это тема для отдельной статьи, а просто приведу один из примеров того, как можно сократить код.

Пусть у нас есть список рабочих (Employee), нужно выбрать из них подмножество, удовлетворяющее определенному критерию выбора: это может быть зарплата, возраст или отдел.

List getStaffWithMuchSalaryThan(List staff, double salary);
List getStaffYoungerThan(List staff, int age);
List getStaffByDepartment(List staff, Dept dept);


В каждом примерно одинаковая логика: пробегаем по циклу, проверяя удовлетворение условиям. Если удовлетворяет — добавляем в результирующий список.
То есть на Java имеем три метода вида:

public List getStaffWithMuchSalaryThan(List staff, double salary) {
 List result = new ArrayList();
 for(Employee e: staff) {
  if(e.getSalary() >= salary) {
   result.add(e);
  }
 }
 return result;
}

и так для каждого из указанных выше еще двух методов. Конечно, проблема дизайна кода может решиться с помощью паттерна Strategy, но количество строк останется довольно большим.

Теперь попробуем написать то же самое, используя замыкания.

//список рабочих
def staff = [
 new Employee(firstName: 'A', salary:500, age:20, dept:'K'),
 new Employee(firstName: 'B', salary:700, age:30, dept:'K'),
 new Employee(firstName: 'C', salary:1000, age:25, dept:'A2') ]

// определение замыкания
def getStaffWithCriteria(staff, criteria) {
 result = []
 for(e in staff) {
 if(criteria(e)) result.add(e)
 }
 result
}

println getStaffWithCriteria(staff, {e -> e.salary > 600}) //зарплата выше 600
println getStaffWithCriteria(staff, {e -> e.age < 27}) //все, кто моложе 27 лет
println getStaffWithCriteria(staff, {e -> e.dept == 'K'}) //работающие в отделе K


// UPD: И еще без одного велосипеда код упрощается до:

println staff.grep {e -> e.salary > 600}
println staff.grep {e -> e.age < 27}
println staff.grep {e -> e.dept == 'K'}

Спасибо thevery

Результат:
[B, C]
[A, C]
[A, B]


Работа с коллекциями


Groovy сильно упрощает работу со списками, начиная от их создания до итерации по ним.

Вполне логичное:
def list = ['a', 'b', 'c']

вместо кода на Java:

List list = new ArrayList();
list.add(«a»);
list.add(«b»);
list.add(«c»);

Предположим нам надо просто вывести список элементов:
в Java:
for(String s: list) {
 System.out.println(s);
}

в Groovy:
list.each {println it}

Цикл на Java будет не таким уже привлекательным, если нам необходим доступ к индексу элемента. Тогда не обойтись без цикла вида, представленного ниже, или введя дополнительную переменную, вместе с этим выполняя операцию инкремента над ней внутри цикла:
for (int i = 0; i < list.size(); i++) {
 System.out.println(i + " - " + list.get(i));
}


В Groovy и эта задача имеет более простое решение

list.eachWithIndex {e, i -> println “${i} - ${e}”}

Также когда-то всем приходилось писать код для вывода списка с разделителями, с условием, что после последнего элемента разделителя быть не должно. И вот, это одно, режущее глаза условие на проверку, является ли элемент последним в цикле.

Код на groovy для этой цели сильно упрощается с использованием метода join

list.join(', ')

произведет строку: “a, b, c”

Сборка проекта


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

Заключение


Безусловно, эта статья не претендует на титул изречений гуру или основное руководство, побуждающее использовать Groovy. Больше хотелось просто поделиться новыми впечатлениями. Но будет очень неплохо, если дальнейшее изучение этого языка позволит вам остаться приверженцем платформы Java, внеся при этом много динамических возможностей от Groovy. Здесь была рассмотрена лишь малая часть того, как Groovy может помочь сделать код более простым и элегантным, но я надеюсь, что часть читателей на этом не остановятся и попробуют множество других вкусностей, предлагаемых этим языком. В завершение можно обратить внимание на то, что Groovy — язык молодой и в настоящее время развивается довольно активно своим комьюнити и отчасти благодаря инвестициям со стороны SpringSource. Де-факто веб-фреймворком для языка является Grails.
Основные источники

groovy.codehaus.org
www.infoq.com/presentations/Transforming-to-Groovy (смены картинок презентации не дождетесь, можно смело жать fullscreen на видео)
www.infoq.com/presentations/Industrial-Strength-Groovy
www.asert.com.au/pubs/Groovy/Groovy.pdf
Теги:
Хабы:
Всего голосов 48: ↑39 и ↓9+30
Комментарии86

Публикации

Истории

Работа

Java разработчик
350 вакансий

Ближайшие события