Практически Groovy: Программирование MVC с помощью шаблонов Groovy

Упростите представление отчетов с помощью среды механизмов шаблонов Groovy

Представления являются неотъемлемой частью модели программирования MVC, которая сама по себе является распространённым компонентом разработки корпоративных приложений. В этом выпуске Практически Groovy Эндрю Гловер покажет, как механизм шаблонов Groovy может упростить программирование представлений и упростить поддержку кода в долгосрочной перспективе.

Эндрю Гловер, президент компании, Stelligent Incorporated

Эндрю ГловерЭндрю Гловер является президентом компании Stelligent Incorporated , которая помогает другим фирмам решать проблемы качества программного обеспечения. Для этого используются эффективные стратегии тестирования и технологии непрерывной интеграции, которые позволяют коллективам разработчиков постоянно контролировать качество кода, начиная с ранних стадий разработки. Просмотрите блог Энди , там можно найти список его публикаций.



23.09.2008

В последних статьях серии Практически Groovy я показал, что Groovy -- это отличный инструмент для создания приложений, формирующих отчёты. Я приводил пример для составления отчетов по контрольным суммам, чтобы проиллюстрировать основы написания сценариев Ant в Groovy, и отчетов по статистике базы данных, чтобы продемонстрировать преимущества программирования JDBC с помощью GroovySql. Программирование JDBC с помощью GroovySql . Оба эти примера отчетов формируются в сценарии Groovy и оба по своей природе имеют связанные с ними "представления".

В примере отчета по контрольным суммам код представления был несколько запутан. Хуже того, он обещал сложности в поддержке: Если бы мне когда-либо понадобилось изменить какой-либо аспект представления, мне пришлось бы изменить сам код сценария. Поэтому в этом месяце я покажу, как упростить представление отчета с помощью механизма шаблонов Groovy и приложения формирования отчета по контрольным суммам из моей предыдущей статьи.

Механизмы шаблонов подобны XSLT и могут формировать код в любом формате, для которого определен шаблон, в том числе XML, HTML, SQL и Groovy. Подобно JSP, механизмы шаблонов могут упростить выделение элементов представления в отдельные сущности, такие как файл шаблона JSP. Например, если предполагается вывод отчета в XML, вы можете создать шаблон XML, в котором будут содержаться подстановочные символы, заменяемые значениями во время работы. После этого механизм шаблонов преобразует шаблон, считывая его и заполняя подстановочные символы соответствующими значениями. В результате на выходе будет получен документ в формате XML.

Об этой серии руководств

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

Перед тем, как приступать к рассмотрению шаблонов Groovy, я хотел бы вернуться ненадолго к понятию типа String в Groovy. Все языки сценариев пытаются сделать работу со строками максимально простой. И снова Groovy нас не подводит. Прежде чем мы продолжим, нажмите на пиктограмму Code в верхней или нижней части этой страницы (или перейдите к разделу Материалы для загрузки) для загрузки исходного кода к этой статье.

Мне не хватает Strings

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

Если вы указываете многострочную String в обычном коде Java, вам нужно завершать строки множеством докучливых +, правильно? Но, как вы можете увидеть в листинге 1, в Groovy этих + нет, что делает код значительно чище и проще.

Листинг 1. Многострочные строки в Groovy
String example1 = "This is a multiline
  string which is going to
  cover a few lines then
  end with a period."

Как показано в листинге 2, Groovy также поддерживает понятие here-docs. here-doc -- это удобный механизм создания форматированных String, например, HTML и XML. Обратите внимание, что синтаксис here-doc не сильно отличается от обычного объявления String, за исключением того, что в нем обязательно использование тройных кавычек, как в Python.

Листинг 2. Here-docs в Groovy
itext =
"""
This is another multiline String
that takes up a few lines. Doesn't
do anything different from the previous one.
"""

Для реализации подстановок во время выполнения программы в Groovy используются GString. Если вы не знакомы с ними, позволю себе предположить, что вы уже видели их раньше, и даже, вероятно, использовали их. Например, GString позволяют выполнять подстановку значений с помощью bash-подобного синтаксиса ${}. Прелесть GString состоит в том, что вам не надо даже задумываться о том, что вы используете тип GString; вы просто пишете код с String в Groovy, как в коде Java.

О механизмах шаблонов

Механизмы шаблонов существуют уже давно и присутствуют почти в любом современном языке программирования. В обычном языке Java, например, есть Velocity и FreeMarker; в Python есть Cheetah, а в Ruby - ERB; в Groovy тоже есть собственный механизм шаблонов. Ссылки на дополнительную информацию о механизмах шаблонов можно найти в разделе Ресурсы.

Листинг 3. GStrings в Groovy
lang = "Groovy"
println "Uncle man, Uncle man, I dig ${lang}."

В листинге 3 я создал переменную lang и присвоил ей значение "Groovy". Я вывожу String типа GString и заменяю место после слова "dig" значением ${lang}. В реальности этот код вывел бы "Uncle man, Uncle man, I dig Groovy". Стильно, не правда ли?

Подстановка во время выполнения программы фактически является общей чертой динамических языков, и здесь, как обычно, Groovy идёт на шаг впереди. GString в Groovy позволяют автоматически вызывать методы для заменяемых значений, что открывает широчайшие возможности для создания динамических текстов. Например, в листинге 4 я могу вызвать метод для нужной мне переменной (в данном случае метод length()) на объект типа String.

Листинг 4. Автоматический вызов GString
lang = "Groovy"
println "I dig any language with ${lang.length()} characters in its name!"

Код в листинге 4 выведет "I dig any language with 6 characters in its name!" В следующем разделе я покажу вам, как использовать функцию вызова Groovy для реализации некоторых более сложных возможностей шаблонов.


Шаблоны Groovy

Работу с шаблонами можно разделить на две основные задачи: Во-первых, нужно создать шаблон; во вторых, нужно реализовать код отображения. Создание шаблонов в среде шаблонов Groovy очень похоже на создание JSP, поскольку вы можете использовать синтаксис JSP. Ключ к созданию этих шаблонов лежит в определении переменных, заменяемых в ходе работы программы. Например, в листинге 5 я определил шаблон для создания GroovyTestCase.

Листинг 5. Шаблон для создания GroovyTestCases
import groovy.util.GroovyTestCase
class <%=test_suite %> extends GroovyTestCase {
  <% for(tc in test_cases) {
     println "\tvoid ${tc}() { } "
  }%>
}

Шаблон, показанный в листинге 5, выглядит как файл JSP, поскольку я использовал синтаксис <% и <%=. Однако, благодаря гибкости Groovy, вы не ограничены синтаксисом JSP. Вы также можете использовать превосходные GString Groovy, как показано в листинге 6.

Листинг 6. GStrings в действии
<person>
  <name first="${p.fname}" last="${p.lname}"/>
</person>

В листинге 6 я создал простой шаблон, представляющий документ XML, который определяет коллекцию элементов person . Как вы можете видеть, шаблон ожидает некоторый объект p, у которого будут свойства fname и lname .

Шаблоны в Groovy определяются очень просто, как это и должно быть. Шаблоны Groovy - это совсем не ядерная физика. Это просто способ упрощения отделения представления от его модели. Следующий шаг состоит в написании кода для выполнения отображения в процессе работы программы.


Отображение в процессе работы программы

Теперь, когда у меня есть шаблон, мне нужно наложить определенные мной переменные на значения, полученные в процессе работы программы. Как это обычно бывает с Groovy, вещи, которые кажутся сложными, на самом деле очень просты. Все, что нужно сделать -- установить соответствие (map) ключей, которые являются именами переменных в шаблоне, и ключей, значения которых являются нужными значениями, подставляемыми в процессе работы.

Например, если в простом шаблоне есть переменная favlang, мне нужно определить map со значением ключа favlang. Значением ключа будет мой любимый язык сценариев (в данном случае, конечно же, Groovy).

В листинге 7 я определил этот простой шаблон, а в листинге 8 показан соответствующий код отображения.

Листинг 7. Простой шаблон для демонстрации отображения
My favorite dynamic language is ${favlang}

В листинге 8 показан пример класса, который делает пять вещей, в том числе две очень важные. Можете ли вы назвать их?

Листинг 8. Отображение значений для простого шаблона
package com.vanward.groovy.tmpl
import groovy.text.Template
import groovy.text.SimpleTemplateEngine
import java.io.File
class SimpleTemplate{
  static void main(args) {
    fle = new File("simple-txt.tmpl")
    binding = ["favlang": "Groovy"]
    engine = new SimpleTemplateEngine()
    template = engine.createTemplate(fle).make(binding)
    println template.toString()
  }
}

Отображение значений для шаблона, выполненное в листинге 8, оказалось на удивление простым.

Сначала я создал экземпляр File , указывающий на шаблон simple-txt.tmpl.

После этого я создал связывающий объект binding, который, по существу, является map. Я отобразил значение favlang в шаблоне на String Groovy. Это отображение - первый важный шаг использования шаблонов в Groovy, и, на самом деле, в любом другом языке, где есть механизм шаблонов.

Затем я создал экземпляр SimpleTemplateEngine, который является частной реализацией механизма шаблонов в Groovy. После я передал механизму экземпляр шаблона (simple-txt.tmpl) и объект binding. Объединение шаблона и соответствующего объекта binding - это второй важный шаг в листинге 8 и основной вопрос работы с механизмами шаблонов. По сути дела, среда будет отображать значения, найденные в объекте binding, на имена в соответствующем шаблоне.

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


Более сложный шаблон

В листинге 9 я создал класс Person, представляющий элемент person, определенный в листинге 6..

Листинг 9. Класс Person в Groovy
class Person{
 age
 fname
 lname
 String toString(){
  return "Age: " + age + " First Name: " + fname + " Last Name: " + lname
 }
}

В листинге 10 вы можете увидеть код отображения, который устанавливает соответствие для определенного выше класса Person.

Листинг 10. Отображение класса Person на шаблон
import java.io.File
import groovy.text.Template
import groovy.text.SimpleTemplateEngine
class TemplatePerson{
  static void main(args) {
    pers1 = new Person(age:12, fname:"Sam", lname:"Covery")
    fle = new File("person_report.tmpl")
    binding = ["p":pers1]
    engine = new SimpleTemplateEngine()
    template = engine.createTemplate(fle).make(binding)
    println template.toString()
  }
}

Код, приведенный выше, кажется знакомым, не так ли? На самом деле он похож на код листинга 8, с добавлением строки, создающей экземпляр pers1. Давайте теперь ещё раз ненадолго вернёмся к шаблону, приведенному в листинге 6. Видите, как в шаблоне указываются свойства fname и lname? Я просто создал экземпляр Person, присвоив свойству fname значение "Sam" и свойству lname значение "Covery" .

При выполнении кода, приведенного в листинге 10, на выходе будет сформирован XML , определяющий элемент person, как видно в листинге 11.

Листинг 11. Вывод шаблона Person
<person>
  <name first="Sam" last="Covery"/>
</person>

Отображение списка

В листинге 5 я определил шаблон для GroovyTestCase. Если вы посмотрите на шаблон сейчас, вы заметите, что в определении есть некая логика для выполнения итераций по коллекции. В листинге 12 приведен похожий код, но в этом коде имеется логика для отображения list тестовых случаев.

Листинг 12. Отображение списка тестовых случаев
fle = new File("unit_test.tmpl")
coll = ["testBinding", "testToString", "testAdd"]
binding = ["test_suite":"TemplateTest", "test_cases":coll]
engine = new SimpleTemplateEngine()
template = engine.createTemplate(fle).make(binding)
println template.toString()

Если посмотреть на листинг 5, можно увидеть, что шаблон ожидает list с именем "test_cases" -- который я определил в листинге 12 как coll , содержащий три элемента. Я просто присвоил coll ключу "test_cases" в объекте binding - и код готов.

Теперь должно быть понятно, насколько просто использовать шаблоны в Groovy. Они также облегчают применение популярного шаблона MVC и, что еще важнее, поддерживают реорганизацию исходного кода в код MVC путём реализации представления. В следующем разделе я покажу, как реорганизовать код примера из предыдущей статьи, применяя полученные здесь навыки.


Реорганизация кода с применением шаблонов

В колонке по написанию сценариев Ant с помощью Groovy я приводил простую утилиту, которая формировала отчёт по контрольным суммам файлов класса. Если помните, я довольно топорно написал XML с помощью println. Этот код был настолько безобразным, что даже мне пришлось это признать. Убедитесь сами, посмотрев листинг 13.

Листинг 13. Корявый код
nfile.withPrintWriter{ pwriter |
  pwriter.println("<md5report>")
    for(f in scanner){
       f.eachLine{ line |
         pwriter.println("<md5 class='" + f.path + "' value='" + line + "'/>")
       }
    }
  pwriter.println("</md5report>")
}

Вспомним еще раз контекст задачи: код, приведенный в листинге 13, принимает некоторые данные и записывает их в файл (экземпляр nfile с помощью PrintWriter. Обратите внимание, что я жёстко закодировал компонент представления отчёта (XML) в println. Проблема этого подхода в отсутствии гибкости. Если когда-нибудь позже мне понадобится внести изменения, мне нужно будет вмешиваться в логику сценария Groovy. Хуже того - представьте, что изменения захочет внести непрограммист. Код Groovy будет для него поистине устрашающим.

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

Определение шаблона

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

Листинг 14. Реструктуризация старого кода в шаблон
<md5report>
<% for(clzz in clazzes) {
  println "<md5 class=\"${clzz.name}\" value=\"${clzz.value}\"/>"
}%>
</md5report>

Шаблон, определенный в листинге 14, похож на шаблон для GroovyTestCase в том, что в него входит логика итерирования по коллекции. Обратите внимание, что я смешал синтаксис JSP c GString.

Написание кода отображения

Теперь, когда определен шаблон, нужно написать код отображения в процессе работы программы. Мне нужно заменить старую логику записи файла в коде формированием коллекции объектов ChecksumClass и помещением этих объектов в объект binding.

После этого модель становится классом ChecksumClass, определенным в листинге 15.

Листинг 15. Определение CheckSumClass в Groovy
class CheckSumClass{
  name
  value
  String toString(){
   return "name " + name + " value " + value
  }
}

Классы в Groovy определяются очень просто, не правда ли?

Создание коллекции

Теперь мне нужно изменить раздел кода, ранее выполнявший вывод в файл - на этот раз заменив его логикой, наполняющей список новыми ChecksumClass, как показано в листинге 16.

Листинг 16. Измененный код, создающий коллекцию ChecksumClasses
clssez = []
for(f in scanner){
  f.eachLine{ line |
   iname = formatClassName(bsedir, f.path)
   clssez << new CheckSumClass(name:iname, value:line)
  }
}

Из листинга 16 видно, как просто использовать синтаксис, подобный Ruby, для добавления объектов в list - и это на самом деле чрезвычайно удобно. Сначала я создал list с помощью синтаксиса []. После этого я использовал краткую мнемонику цикла for, после которой указал итератор с замыканием. Замыкание обрабатывает каждый line, который в данном случае является значением контрольной суммы, и создаёт экземпляр только что определенного CheckSumClass (с помощью конструктора, автоматически созданного Groovy) и добавляет его в коллекцию. Неплохо - и приятно в написании.

Добавление отображения шаблона

Последнее, что мне нужно сделать - добавить код механизма шаблона. Этот код будет выполнять отображение в процессе работы программы и запись отформатированного соответствующим образом шаблона в исходный файл, как показано в листинге 17.

Листинг 17. Реструктуризация кода с отображением шаблона
fle = new File("report.tmpl")
binding = ["clazzes": clzzez]
engine = new SimpleTemplateEngine()
template = engine.createTemplate(fle).make(binding)
nfile.withPrintWriter{ pwriter |
  pwriter.println template.toString()
}

Теперь код, приведенный в листинге 17, не должен быть для вас новостью. Я взял list из листинга 16 и поместил его в объект binding. После этого я взял объект nfile и записал соответствующий вывод отображенного шаблона из листинга 14.

Перед тем, как свести всё вместе в листинге 18, вам, возможно, захочется ещё раз взглянуть на тот корявый код из листинга 13, с которого мы начали.. И, вот, для сравнения, как выглядит реорганизованный код:

Листинг 18. Смотрите! Получилось не так плохо!
/**
 *
 */
buildReport(bsedir){
 ant = new AntBuilder()
 scanner = ant.fileScanner {
   fileset(dir:bsedir) {
     include(name:"**/*class.md5.txt")
   }
 }
 rdir = bsedir + File.separator + "xml" + File.separator
 file = new File(rdir)
 if(!file.exists()){
   ant.mkdir(dir:rdir)
 }
 nfile = new File(rdir + File.separator + "checksum.xml")
 clssez = []
 for(f in scanner){
   f.eachLine{ line |
    iname = formatClassName(bsedir, f.path)
    clssez << new CheckSumClass(name:iname, value:line)
   }
 }
 fle = new File("report.tmpl")
 binding = ["clazzes": clzzez]
 engine = new SimpleTemplateEngine()
 template = engine.createTemplate(fle).make(binding)
 nfile.withPrintWriter{ pwriter |
   pwriter.println template.toString()
 }
}

Итак, хотя я и не претендовал на написание изящного кода, получившийся код определенно не такой безобразный, как старый. И теперь, чтобы завершить начатое, мне остаётся заменить несколько старых кривых println более красивым кодом шаблона Groovy. (Эксперты в реорганизации кода отметят, что для улучшения кода я мог бы применить метод Extract.)


Заключение

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

В следующем месяце я собираюсь показать вам, как использовать Groovy для создания Web-приложений посредством Groovlets. А пока - удачной разработки шаблонов с помощью Groovy!


Загрузка

ОписаниеИмяРазмер
Образец кодаj-pg02155-source.zip2.18 KB

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=340787
ArticleTitle=Практически Groovy: Программирование MVC с помощью шаблонов Groovy
publish-date=09232008