Статьи

Простой, но мощный DSL с использованием Groovy

В одном из моих проектов у нас была очень сложная доменная модель, которая включала в себя более ста различных типов доменных объектов. Это был чистый проект Java, и, честно говоря, Java очень многословна в отношении свойств объекта, инициализации и настройки. Внезапно появилось новое требование, позволяющее пользователям определять и использовать собственные объектные модели. Итак … путешествие началось.

Мы пришли к мысли, что необходим какой-то предметный язык для описания всех этих типов объектов и отношений. Здесь Groovy пришел на помощь. В этом посте я хотел бы продемонстрировать, насколько мощным и выразительным может быть простой DSL, написанный с использованием сборщиков Groovy .
Как всегда, давайте начнем с файла POM для нашего примера проекта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
4.0.0
 
com.example
dsl
0.0.1-SNAPSHOT
jar
 
 
 UTF-8
 
 
 
  
  junit
  junit
  4.10
  
  
  org.codehaus.groovy
  groovy-all
  1.8.4
  
 
 
 
  
   
   org.codehaus.gmaven
   gmaven-plugin
   1.4
    
     
      
      1.8
       
      
      
      compile
      testCompile
      
     
    
   
 
   
   org.apache.maven.plugins
   maven-compiler-plugin
   2.3.1
    
    1.6
    1.6
    
   
 
   

Я буду использовать последнюю версию Groovy , 1.8.4. Наша модель домена будет включать три класса: Организация , Пользователь и Группа . У каждой организации есть обязательное имя, несколько пользователей и несколько групп. Каждая группа может иметь несколько пользователей в качестве участников. Довольно просто, поэтому вот наши классы Java.
Organization.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example;
 
import java.util.Collection;
 
public class Organization {
 private String name;
 private Collection< User > users = new ArrayList< User >();
 private Collection< Group > groups = new ArrayList< Group >();
  
   public String getName() {
  return name;
 }
 
 public void setName( final String name ) {
  this.name = name;
 }
 
 public Collection< Group > getGroups() {
  return groups;
 }
 
 public void setGroups( final Collection< Group > groups ) {
  this.groups = groups;
 }
 
 public Collection< User > getUsers() {
  return users;
 }
 
 public void setUsers( final Collection< User > users ) {
  this.users = users;
 }
}

User.java

01
02
03
04
05
06
07
08
09
10
11
12
13
package com.example;
 
public class User {
 private String name;
 
 public String getName() {
  return name;
 }
 
 public void setName( final String name ) {
  this.name = name;
 }
}

Группа .java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example;
 
import java.util.Collection;
 
public class Group {
 private String name;
 private Collection< User > users = new ArrayList< User >();
 
 public void setName( final String name ) {
  this.name = name;
 }
 
 public String getName() {
  return name;
 }
 
 public Collection< User > getUsers() {
  return users;
 }
 
 public void setUsers( final Collection< User > users ) {
  this.users = users;
 }
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.dsl.samples
 
class SampleOrganization {
 def build() {
  def builder = new ObjectGraphBuilder(
   classLoader: SampleOrganization.class.classLoader,
   classNameResolver: "com.example"
  )
 
  return builder.organization(
   name: "Sample Organization"
  ) {
   users = [
    user(
     id: "john",
     name: "John"
    ),
 
    user(
     id: "samanta",
     name: "Samanta"
    ),
 
    user(
     id: "tom",
     name: "Tom"
    )
   ]
 
   groups = [
    group(
     id: "administrators",
     name: "administrators",
     users: [ john, tom ]
    ),
    group(
     id: "managers",
     name: "managers",
     users: [ samanta ]
    )
   ]
  }
 }
}

И вот небольшой тестовый пример, который подтверждает, что наша модель предметной области соответствует ожиданиям:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package com.example.dsl
 
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
 
import org.junit.Test
 
import com.example.dsl.samples.SampleOrganization
 
class BuilderTestCase {
 @Test
 void 'build organization and verify users, groups' () {
  def organization = new SampleOrganization().build()
 
  assertEquals 3, organization.users.size()
  assertEquals 2, organization.groups.size()
  assertEquals "Sample Organization", organization.name
 }
}

Я использую этот простой DSL снова и снова во многих проектах. Это действительно упрощает создание множества сложных объектных моделей.

Справка: простой, но мощный DSL с использованием Groovy от нашего партнера по JCG