neděle 27. května 2012

Grails - jak jsou naprogramovány mapping v doménových třídách

Tento článek je o jedné zajímavosti z knihy Programming Grails (koupil jsem si Early Release). V Grails existuje jednoduchý způsob definování mapování pro doménové třídy pomocí closure static mapping = {}. Doteď jsem neměl ani páru, jak je to udělané, ani jsem se o to nezajímal. Teď už chápu o trochu více, i když ne úplně vše.

Closure (česky uzávěry) umožňují uvnitř nich volat metody. Příklad:

class SomeClass {
   void callClosure() {
      def dog = {
         woof() // volání metody void woof() z closure
      }
      dog() // zavolá closure
   }
   void woof() {
      println "Woof!"
   }
}

Každý closure má obsažené 3 různé atributy: Owner, Delegate a this. this odkazuje na instanci třídy, kde je closure definován. Owner obsahuje odkaz na instanci, kde je obsažen closure. Ve většině případů je tedy this stejný jako owner. Výjimkou je třeba tento případ (z knihy Programming Groovy):

examiningClosure() {
    println "In First Closure:"
    println "class is " + getClass().name
    println "this is " + this + ", super:" + this.getClass().superclass.name // this = owner
    println "owner is " + owner + ", super:" + owner.getClass().superclass.name
    println "delegate is " + delegate +
    ", super:" + delegate.getClass().superclass.name
    examiningClosure() {  // closure uvnitř closure
        println "In Closure within the First Closure:"
        println "class is " + getClass().name
        println "this is " + this + ", super:" + this.getClass().superclass.name // this != owner
        println "owner is " + owner + ", super:" + owner.getClass().superclass.name
        println "delegate is " + delegate +
        ", super:" + delegate.getClass().superclass.name
    }
}
Výstup:

In First Closure:
class is ThisOwnerDelegate$_run_closure1
this is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
owner is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
delegate is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
In Closure within the First Closure:
class is ThisOwnerDelegate$_run_closure1_closure2
this is ThisOwnerDelegate@55e6cb2a, super:groovy.lang.Script
owner is ThisOwnerDelegate$_run_closure1@15c330aa, super:groovy.lang.Closure
delegate is ThisOwnerDelegate$_run_closure1@15c330aa, super:groovy.lang.Closure

 Atribut Delegate v closure je ten, který právě umožňuje dělat kouzla jako v static mapping = {}. Closure totiž umožňuje nastavit atribut Delegate na jiný objekt pomocí setDelegate(Object delegate) a pak volat jejich metody. V praxi to znamená toto:


Doménová třída s defaultním řazením entit podle dne:
class Attendance {
    Date day
    static mapping = {
        sort("day")
    }
}
V doménové třídě však žádná metoda sort defaultně neexistuje. Avšak třída je platná a funkční. Pokud použijete debugger a zastavíte zpracování v closure mappings, uvidíte následující:
this = class wellness.Attendance
owner = class wellness.Attendance
delegate = org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateMappingBuilder@3321875 // ha, to je on
Když si otevřete třídu HibernateMappingBuilder, nacházejí se tam metody, které se používají v mapping, jako je výše zmíněný sort, cache, table, version apod. To je celé tajemství.

P.S. Další otázkou je, kde je nastaven setDelegate() v doménových třídách? To jsem se ještě nedočetl (ale myslím, že je to udělané pomocí MOP). O tom napíšu, až budu vědět na stopro nebo si to někde přečtu.

Žádné komentáře:

Okomentovat