Быть в 10 раз эффективнее благодаря Groovy

Содержание

Слайд 2

Smart1: система бронирования ТВ-рекламы

Вся реклама на телеканалах 1+1, 2+2, ТЕТ, CITI продается

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

Архитектура CentOS 4.1.2 PostgreSQL 8.4.4 Tomcat 6.0.20 Hibernate 3.3.x Ehcache 2.1.0 Java
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 Тестов
Время сборки

Разработка Java 1.6 64-Bit server IntelliJ IDEA 10 GIT Maven 2 Jetbrains
40 мин
Установка на рабочий сервер 2 раза в неделю

Слайд 6

Производительность

Денормализация структуры БД
Тяжелые отчеты обновляются по расписанию
Ряд задач выполняется только ночью  

HP

Производительность Денормализация структуры БД Тяжелые отчеты обновляются по расписанию Ряд задач выполняется
Proliant DL360G6
2xQuad CPU
12G RAM

Пиковая нагрузка 40 gwt rpc запросов в секунду

Слайд 7

Строки кода

Строки кода

Слайд 8

От Java к Groovy

Smart1 - наш второй groovy проект
До перехода сомнения:
что такого

От Java к Groovy Smart1 - наш второй groovy проект До перехода
принципиального может дать groovy?
зачем терять часть возможностей IDE?
огромный тормоз
После перехода:
сожаление, что gwt не позволяет использовать groovy, чтобы полностью отказаться от java

Слайд 9

Опрос: Насколько Groovy эффективнее Java?

4-6 раз, коллеги
Я бы сказал 2-3 раза, Алекс Ткачман
Я обычно

Опрос: Насколько Groovy эффективнее Java? 4-6 раз, коллеги Я бы сказал 2-3
продуктивнее в 2 с лишним. Иногда groovy действительно упрощает проблему и я становлюсь в 3-5 раз продуктивнее. Давид Кларк
Моя продуктивность легко достигает 10 раз. Jochen Theodorou  

Слайд 10

Groovy - это гораздо больше, чем убрать из Java ; и типы!

значительно меньше кода
код

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())

List bookedPlacements = new ArrayList(); for (Placement placement : placements) { if
{
   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

Коротко и выразительно! Вернуть короткие названия бюджетных месяцев def monthNames = budgets*.month*.shortName
= 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

List afterBlocks = new ArrayList (); for (Block block : blocks) {
(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
&&

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

Немного сложнее? Взять размещения из самой популярной категории placements.groupBy { it.category }.collect {it} .sort {it.value.size()}.last().value
{it.value.size()}.last().value

Слайд 19

Java, с использованием «библиотечных» groupBy и last:

List groupsList = new ArrayList((Util.groupBy(placements,

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 :

Java, прямая реализация: Map > categoryPlacements = new HashMap >(); for (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 Настоящие возможности открываются, когда мы понимаем что такое Closure sort,
и т.п – все навсего методы принимающие Closure и мы можем делать такие свои

Слайд 22

Сила Closure

Получить Map время, на название (названия уникальны для времени)

placements.mapUnique(‘time’) { it.name

Сила Closure Получить Map время, на название (названия уникальны для времени) placements.mapUnique(‘time’)
}

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()
}

Расширение существующих классов Методы у Object дают нам следующий синтаксис: transaction { new User(‘New User’).dbStore() }

Слайд 25

Расширение существующих классов

Сделаем немного удобнее Hibernate Criteria API:

def clientGroups =
RobotGroup.dbQuery.with {
eq('deleted',

Расширение существующих классов Сделаем немного удобнее Hibernate Criteria API: def clientGroups =
false)
createCriteria('copy').eq('_client', client)
}.list()

Слайд 26

DSL делается легко

count = 0
new SwingBuilder().edt {
frame(title: 'Frame', size: [300, 300],

DSL делается легко count = 0 new SwingBuilder().edt { frame(title: 'Frame', size:
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

JFrame frame = new JFrame("Frame"); frame.setSize(300, 300); frame.setLayout(new BorderLayout()); final JLabel label
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)

DSL делается легко shopList(client, 2011, 'Test ShopList', primePercent: 70) { discount(offPrime: -10.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),

Selenium junit тест void testPlace_SpotClient() { runTest( agent, { chooseCampaigns 'XC' placeClick
'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

Bidirectional Association class Program { @OneToMany (mappedBy = "_program") Set _blocks =
Block {
@ManyToOne
    Program _program
}

Слайд 32

Bidirectional Association

И теперь мы сразу можем работать:

def p = new Program()
def b

Bidirectional Association И теперь мы сразу можем работать: def p = new
= new Block()
p << b
p.addBlock(b)
p.removeBlock(b)
//valid properties: p.blocks (unmodifiable set), b.program

Слайд 33

Bidirectional Association

Этого писать не нужно:

class Program {
...
void addBlock(Block b) {
 

Bidirectional Association Этого писать не нужно: class Program { ... void addBlock(Block
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

Lazy initialization Этого писать не нужно: class MonthBudget { ... Centi __actualBudget()
{
...
 Centi _actualBudget
 def getActualBudget() {
   if (_actualBudget == null) {
      _actualBudget = ...  
   }
 }
}

println new MonthBudget().actualBudget

Слайд 35

Но не все так хорошо

Скорость?
IDE?

Но не все так хорошо Скорость? IDE?

Слайд 36

Реально тормоз!

Groovy работает в 10 раз медленнее Java

Реально тормоз! Groovy работает в 10 раз медленнее Java

Слайд 37

Benchmark Groovy, Grovy++, Java

https://github.com/alextkachman/fib-benchmark

Benchmark Groovy, Grovy++, Java https://github.com/alextkachman/fib-benchmark

Слайд 38

Но на этом можно работать

Groovy работает также как Python, Ruby, PHP и

Но на этом можно работать Groovy работает также как Python, Ruby, PHP и т.п.
т.п.

Слайд 39

Benchmark Java, Python, Ruby

http://shootout.alioth.debian.org/

Benchmark Java, Python, Ruby http://shootout.alioth.debian.org/

Слайд 40

Скорость Groovy

не забываем, что часто узкое место база данных
любой фрагмент можно переписать

Скорость Groovy не забываем, что часто узкое место база данных любой фрагмент
на java
любой фрагмент можно переписать сделать Groovy++

Слайд 41

Groovy++

Статически типизированное расширение Groovy
По скорости выполнения почти не уступает Java
Может рассматриваться как

Groovy++ Статически типизированное расширение Groovy По скорости выполнения почти не уступает Java
альтернатива Scala
Пишется небольшой группой энтузиастов (один хакер?), мало используется

Слайд 42

IDEA

IDEA в целом очень хорошо поддерживает groovy:
Для работы с динамическими методами и

IDEA IDEA в целом очень хорошо поддерживает groovy: Для работы с динамическими
полями в IDEA есть Dynamic properties
Работает выведение типов, в основном ☺
Тем не менее:
Для динамики мы теряем автоматический рефакторинг и высокоуровневый поиск (findUsages)
В отладчике иногда сильно тормозит Step Into
Имя файла: Быть-в-10-раз-эффективнее-благодаря-Groovy.pptx
Количество просмотров: 46
Количество скачиваний: 0