Слайд 1 Быть в 10 раз эффективнее благодаря Groovy Евгений Компаниец
Слайд 2 Smart1: система бронирования ТВ-рекламы Вся реклама на телеканалах 1+1, 2+2, ТЕТ,
CITI продается через Smart1 Месячный оборот 00 000 000 гр. Информация о
1 300 000 размещениях рекламы Сложная модель продаж - аукцион Отчеты Интеграция с внешними системами:
GFK Mark Data Media Workstation, 1C 2 разработчика; 1,5 года; внедрено на втором месяце разработки
Слайд 4 Архитектура CentOS 4.1.2 PostgreSQL 8.4.4 Tomcat 6.0.20 Hibernate 3.3.x Ehcache 2.1.0 Java 1.6 64-Bit server GWT 2.3.0 GWT
2.3.0 RPC Servlet Groovy 1.8.0 Daemon Servlets
Слайд 5 Разработка Java 1.6 64-Bit server IntelliJ IDEA 10 GIT Maven 2 Jetbrains TeamCity 6.0.3 Selenium 1.0.2 710
Тестов Время сборки 40 мин Установка на рабочий сервер 2 раза в
неделю
Слайд 6 Производительность Денормализация структуры БД Тяжелые отчеты обновляются по расписанию Ряд задач выполняется только
ночью HP Proliant DL360G6 2xQuad CPU 12G RAM Пиковая нагрузка 40 gwt rpc
запросов в секунду
Слайд 8 От Java к Groovy Smart1 - наш второй groovy проект До перехода
сомнения: что такого принципиального может дать groovy? зачем терять часть возможностей IDE? огромный
тормоз После перехода: сожаление, что gwt не позволяет использовать groovy, чтобы полностью отказаться от java
Слайд 9 Опрос: Насколько Groovy эффективнее Java? 4-6 раз, коллеги Я бы сказал 2-3 раза, Алекс
Ткачман Я обычно продуктивнее в 2 с лишним. Иногда groovy действительно
упрощает проблему и я становлюсь в 3-5 раз продуктивнее. Давид Кларк Моя продуктивность легко достигает 10 раз. Jochen Theodorou
Слайд 10 Groovy - это гораздо больше, чем убрать из Java ; и типы! значительно
меньше кода код значительно читабельнее значительно выше повторное использование легко создаются DSL не нужен
псевдокод
Слайд 11 Коротко и выразительно! Взять все проходящие размещения и отсортировать сначала по
цене, потом по дате создания placements.findAll { it.booked } .sort
{p1, p2 -> p2.wPrice <=> p1.wPrice ?: p1.creationDate <=> p2.creationDate }
Слайд 12 List bookedPlacements = new ArrayList(); for (Placement placement : placements) {
if (placement.isBooked()) { bookedPlacements.add(placement);
}
}
Collections.sort(bookedPlacements, new Comparator
() { public int compare(Placement p1, Placement p2) { int r = p1.getwPrice().compareTo(p2.getwPrice()); if (r == 0) { r = p1.getCreationDate() .compareTo(p2.getCreationDate()); } return r; } });
Слайд 13 Коротко и выразительно! Вернуть короткие названия бюджетных месяцев def monthNames =
budgets*.month*.shortName List monthNames = new ArrayList(); for (MonthBudget budget: budgets) {
monthNames.add(budget.getMonth().getShortName()); }
Слайд 14 Коротко и выразительно! Эфирное время конца программы – это время начала
первого из послепрограмных блоков, либо время конца программы blocks.findAll { it.position
== AFTER }*.startTime.min() ?: endTime
Слайд 15 List afterBlocks = new ArrayList (); for (Block block : blocks)
{ if (block.getPosition() == AFTER) {
afterBlocks.add(block); } } if (afterBlocks.isEmpty()) { return endTime; } Time minTime = new Time(0); for (Block block : afterBlocks) { if (block.getStartTime().isBefore(minTime)) { minTime = block.getStartTime(); } } return minTime;
Слайд 16 Коротко и выразительно! Если плательщик задан, то вернуть его, иначе взять
плательщика из прошлого периода. Если в прошлом периоде нет плательщиков,
то взять любого из агентства.
payee ?: prevInYear?.payee ?: (agency.payees as List)[0]
Слайд 17 if (payee != null) { return payee; } if (getPrevInYear()
!= null && getPrevInYear().getPayee()
!= null) { return prevInYear.getPayee(); } return getAgency().getPayees().iterator().next();
Слайд 18 Немного сложнее? Взять размещения из самой популярной категории placements.groupBy { it.category }.collect
{it} .sort {it.value.size()}.last().value
Слайд 19 Java, с использованием «библиотечных» groupBy и last: List groupsList = new
ArrayList((Util.groupBy(placements, new GroupSelector() {
public CopyCategory getProperty(Placement p) {
return p.getCopyCategory();
}
})).entrySet());
Collections.sort(groupsList,
new Comparator
>>() { public int compare(Map.Entry> o1, Map.Entry> o2) { return ((Integer) o1.getValue().size()) .compareTo(o2.getValue().size()); } }); return (List) ((Map.Entry) Util.last(groupsList)).getValue();
Слайд 20 Java, прямая реализация: Map categoryPlacements = new HashMap(); for (Placement
placement : placements) { List _placements =
categoryPlacements.get(placement.getCategory()); if (_placements == null) { _placements = new ArrayList(); categoryPlacements.put(placement.getCategory(), _placements); } _placements.add(placement); } CopyCategory popularCategory = null; int maxSize = 0; for (CopyCategory category : categoryPlacements.keySet()) { if (categoryPlacements.get(category).size() > maxSize) { maxSize = categoryPlacements.get(category).size(); popularCategory = category; } } return categoryPlacements.get(popularCategory);
Слайд 21 Сила Closure Настоящие возможности открываются, когда мы понимаем что такое Closure sort,
findAll, groupBy и т.п – все навсего методы принимающие Closure
и мы можем делать такие свои
Слайд 22 Сила Closure Получить Map время, на название (названия уникальны для времени) placements.mapUnique(‘time’)
{ it.name } Map timePlacements =
new HashMap(); for (GfkPlacement placement: placements) { timePlacements.put(placement.time, placement.name); }
Слайд 23 Расширение существующих классов Мы можем добавлять методы и поля к уже
написанным классам без наследования. Наш mapUnique можно вызывать на любой коллекции robot.grp
= 22.centi scheduleMonth.month = 2009.jan block.startTime = /17:59/.time
Слайд 24 Расширение существующих классов Методы у Object дают нам следующий синтаксис: transaction { new
User(‘New User’).dbStore() }
Слайд 25 Расширение существующих классов Сделаем немного удобнее Hibernate Criteria API: def clientGroups = RobotGroup.dbQuery.with
{ eq('deleted', false) createCriteria('copy').eq('_client', client) }.list()
Слайд 26 DSL делается легко count = 0 new SwingBuilder().edt { frame(title: 'Frame',
size: [300, 300], show: true) { borderLayout()
textlabel = label(text: "Click the button!", constraints: NORTH) button(text: 'Click Me', actionPerformed: { count++ textlabel.text = "Clicked ${count} time(s)." }, constraints: SOUTH) } }
Слайд 27 JFrame frame = new JFrame("Frame"); frame.setSize(300, 300); frame.setLayout(new BorderLayout()); final JLabel label =
new JLabel("Click the button"); frame.add(label, NORTH); final JButton button = new JButton("Click
Me"); final int[] counter = {0}; button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { counter[0]++; label.setText("Clicked " + counter[0] + “ time(s)."); } }); frame.add(button, SOUTH); frame.setVisible(true);
Слайд 28 DSL делается легко shopList(client, 2011, 'Test ShopList', primePercent: 70) { discount(offPrime:
-10.disc) channelSl(channel, lowDiscount: -30.disc) {
monthSl(JAN, seasonal: -30.disc) { stdSl(100.centi) lowSl(400.centi) } } }
Слайд 29 Selenium junit тест void testPlace_SpotClient() { runTest( agent, {
chooseCampaigns 'XC' placeClick getBlock(1),
'XC' waitForError 'No Shop List for Volvo in 2009' }, sale, clients, { changeToPerSpot 'Volvo' }, ... ) }
Слайд 30 Динамика Динамическое программирование позволяет нам понять что такое повторное использование по
настоящему! Например давайте перестанем каждый раз делать одно и тоже для
Bidirectional Association и Lazy Initialization:
Слайд 31 Bidirectional Association class Program { @OneToMany (mappedBy = "_program") Set
_blocks = [] } class Block { @ManyToOne Program _program }
Слайд 32 Bidirectional Association И теперь мы сразу можем работать: def p = new
Program() def b = new Block() p
b.program
Слайд 33 Bidirectional Association Этого писать не нужно: class Program { ... void addBlock(Block
b) { b._program = this } void removeBlock(Block b) {
b._program = null
}
}
class Block { ... void setProgram(Program p) { if (_program != null) { _program.friendBlocks .remove(this) } _program = p if (_program != null) { _program.friendBlocks .add(this) } } }
Слайд 34 Lazy initialization Этого писать не нужно: class MonthBudget { ... Centi __actualBudget() { ...
calculation } } class MonthBudget { ... Centi _actualBudget def getActualBudget() { if (_actualBudget ==
null) {
_actualBudget = ...
}
}
}
println new MonthBudget().actualBudget
Слайд 35 Но не все так хорошо Скорость? IDE?
Слайд 36 Реально тормоз! Groovy работает в 10 раз медленнее Java
Слайд 37 Benchmark Groovy, Grovy++, Java https://github.com/alextkachman/fib-benchmark
Слайд 38 Но на этом можно работать Groovy работает также как Python, Ruby,
PHP и т.п.
Слайд 39 Benchmark Java, Python, Ruby http://shootout.alioth.debian.org/
Слайд 40 Скорость Groovy не забываем, что часто узкое место база данных любой фрагмент
можно переписать на java любой фрагмент можно переписать сделать Groovy++
Слайд 41 Groovy++ Статически типизированное расширение Groovy По скорости выполнения почти не уступает Java Может
рассматриваться как альтернатива Scala Пишется небольшой группой энтузиастов (один хакер?), мало
используется
Слайд 42 IDEA IDEA в целом очень хорошо поддерживает groovy: Для работы с динамическими
методами и полями в IDEA есть Dynamic properties Работает выведение типов,
в основном Тем не менее: Для динамики мы теряем автоматический рефакторинг и высокоуровневый поиск (findUsages) В отладчике иногда сильно тормозит Step Into