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

Содержание

Слайд 2

Smart1: система бронирования ТВ-рекламы Вся реклама на телеканалах 1+1, 2+2, ТЕТ,

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

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

продается через Smart1
Месячный оборот 00 000 000 гр.
Информация о 1 300 000 размещениях рекламы
Сложная модель продаж - аукцион 
Отчеты 
Интеграция с внешними системами: GFK Mark Data Media Workstation, 1C
2 разработчика; 1,5 года; внедрено на втором месяце разработки
Слайд 3

Слайд 4

Архитектура CentOS 4.1.2 PostgreSQL 8.4.4 Tomcat 6.0.20 Hibernate 3.3.x Ehcache 2.1.0

Архитектура

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

Разработка

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 запросов в секунду

Слайд 7

Строки кода

Строки кода

Слайд 8

От Java к Groovy Smart1 - наш второй groovy проект До

От Java к Groovy

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

такого принципиального может дать groovy?
зачем терять часть возможностей IDE?
огромный тормоз
После перехода:
сожаление, что gwt не позволяет использовать groovy, чтобы полностью отказаться от java
Слайд 9

Опрос: Насколько Groovy эффективнее Java? 4-6 раз, коллеги Я бы сказал

Опрос: Насколько 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) {

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 =

Коротко и выразительно!

Вернуть короткие названия бюджетных месяцев

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)

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

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

Немного сложнее?

Взять размещения из самой популярной категории

placements.groupBy { it.category }.collect {it}
  

 .sort {it.value.size()}.last().value
Слайд 19

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

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

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

Сила Closure

Настоящие возможности открываются, когда мы понимаем что такое Closure
sort, findAll,

groupBy и т.п – все навсего методы принимающие Closure и мы можем делать такие свои
Слайд 22

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

Сила 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() }

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

Методы у Object дают нам следующий синтаксис:

transaction {
new User(‘New

User’).dbStore()
}
Слайд 25

Расширение существующих классов Сделаем немного удобнее Hibernate Criteria API: def clientGroups

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

Сделаем немного удобнее 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',

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

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:

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'

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

Bidirectional Association

class Program {
@OneToMany (mappedBy = "_program")
    Set _blocks =

[]
}
class Block {
@ManyToOne
    Program _program
}
Слайд 32

Bidirectional Association И теперь мы сразу можем работать: def p =

Bidirectional Association

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

def p = new Program()
def

b = new Block()
p << b
p.addBlock(b)
p.removeBlock(b)
//valid properties: p.blocks (unmodifiable set), b.program
Слайд 33

Bidirectional Association Этого писать не нужно: class Program { ... void

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

Lazy initialization

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

class MonthBudget {
 ...
 Centi __actualBudget() {
    ...  calculation
 }
}

class

MonthBudget {
...
 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 По скорости выполнения почти не уступает

Groovy++

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

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

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

IDEA

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

и полями в IDEA есть Dynamic properties
Работает выведение типов, в основном ☺
Тем не менее:
Для динамики мы теряем автоматический рефакторинг и высокоуровневый поиск (findUsages)
В отладчике иногда сильно тормозит Step Into