Groovy Mixins?

大憨熊 提交于 2019-12-05 01:32:46

Since Groovy 1.6 you can either apply a mixin at compile-time to a class using an annotation

@Mixin(ImagesMixin)
class Person {
}

Or you can apply the mixin at runtime like this:

def myMixin = ImagesMixin
Person.mixin myMixin

The latter approach is more dynamic as the class to mixin can be determined at runtime. Further information about Groovy mixins is available here.

In my experience, a lot of meta-programming of domain classes simply doesn't work. I don't exactly know why, but suspect it's due to the fact these classes are already very heavily meta-programmed by the Grails runtime. In general my approach is

  • Try the meta-programming on a POGO in the Groovy console
  • If that works, try it on a non-domain class in the Grails console
  • If that works, try it on a domain class in the Grails console. If it doesn't work, then it must be because it's a domain class (rather than a problem with the syntax). At this point it's advisable to try and find another way of accomplishing your goal. If that's not possible, then use a combination of the Grails mailing list and/or Stackoverflow and/or the Grails source code to try and get the meta-programming working.

I don't think you are using the proper mixin syntax. Try this:

class MyMixin {
    static doStuff(Person) {
        'stuff was done'
    }
}

class Person {}

Person.mixin MyMixin

new Person().doStuff()

I guess what you've seen there is rather a proposal than a feature ;) Groovy does not support mixins out of the box in this way yet (if ever). But there is a 3rd party lib that can be used to emulate such a behavour: Injecto. And mixins can be defined using AST-Macros in the 1.6 version of Groovy (which is not final yet).

You should always check if your're reading the docs from the real groovy project or from the GroovyJSR project (which is rather a place where proposals are collected).

Another way is to use plain-old MOP to inject behaviour into groovy classes by modifying metaClasses.

Cheers

In case this helps anyone, Following on from @virtualeyes comment above, I've found that

Person.doStuff()

fails unless you call the following first:

new Person().doStuff()
Person.doStuff()

after which, the static method on the class does seem to work (for me, using Grails 2.2.4) I guess it's to do with initialising the class, or something, but I tried:

Person.metaClass.initialize()
Person.doStuff()

and that didn't work!

FYI: There is such a thing as "embedded" domains in Grails now, but it has problems. This is where you can logically include one domain as part of another and have its fields all occur physically in the one DB table. For example, if you find that you have the same subset of fields appearing in multiple tables, like street address/city/state/zip, you could define a StreetAddress domain and embed it. One of the current problems is that Grails will still create a street_address table in addition to embedding its fields in the other tables (unless you play tricks). There are submitted patches pending for this, it seems.

Grails domain objects are already heavily meta-programmed. Instead of the groovy mixin try:

@grails.util.Mixin(ImagesMixin)
    class Person {
}

Use Traits!

Traits are the reason they removed support for mixins, because that are just better. They're basically abstract classes that are implemented. Allowing you to use multiple and operate them as partial classes.

trait A {
    void printSomething() {
        println "foobar"
    }
}

class B implements A {
    void printAnything() {
        printSomething()
    }
}

new B().printAnything() 

Try it out!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!