Easy Singleton in Groovy
Рубрика: Development, Groovy | 30 August 2007, 11:32 |
Vadim Voituk
Не раз слышал мнение, что скриптовые языки с динамической типизацией “учат писать плохо”, в отличие от “статических” языков, которые навязывают использование правильных решений aka шаблоны проектирования.
Позволю себе не согласиться с таким мнением т.к. в “умелых руках” динамические языки позволяют реализовать привычные и известные шаблоны более элегантно и красиво.
Приведу пример реализации шаблона Singleton на Groovy.
Для начала напишем тестовый пример:
[java]
class MyTestClass {
int field
def MyTestClass() {
println "Invoke constructor"
field = 0
}
def method() {
field++
println "field=${field}"
}
}
// TODO: Add singleton behavior
MyTestClass Obj1 = new MyTestClass();
MyTestClass Obj2 = new MyTestClass();
Obj1.method()
Obj1.method()
Obj2.method()
Obj2.method()
[/java]
Запускаем и получаем вполне ожидаемое:
$ groovy Test.groovy
Invoke constructor
Invoke constructor
field=1
field=2
field=1
field=2
Следующим шагом реализуем свой MetaClass.
Тут, наверное, для полного понимания происходящего, стоит сделать небольшой экскурс в понятие MetaClass в Groovy:
Каждый Groovy обьект имеет свой MetaClass (по умолчанию это groovy.lang.MetaClassImpl) – такой себе прокси-класс, через который проходят все вызовы методов на обьекте.
Например код [java]Obj2.method()[/java] на самом деле производит что-то вроде [java]Obj2.getMetaClass().invokeMethod(Obj2, method)[/java].
Возвращаясь к примеру, пишем свою реализацию MetaClass:
[java]
class SingletonMetaClass extends MetaClassImpl {
private instance
def SingletonMetaClass(Class c, Object instance) {
super(c)
this.instance = instance
}
def Object invokeConstructor(Object[] param0) {
return instance;
}
}
[/java]
и собственно класс, который будет делать из обычных классов Singleton-ы:
[java]
class SingletonProducer {
private static me = new SingletonProducer()
static def getInstance() {
return me;
}
private def SingletonProducer() {}
def createSingleton(Class c) {
MetaClass mymeta = new SingletonMetaClass(c, c.newInstance())
GroovySystem.getMetaClassRegistry().setMetaClass(c , mymeta)
}
}
[/java]
И теперь самая главная магия – в первом примере кода вместо строки “// TODO: Add singleton behavior” дописываем
[java]
SingletonProducer single = SingletonProducer.getInstance() single.createSingleton(MyTestClass.class)
[/java]
Запускаем и видим что при каждом создании экземпляра MyTestClass мы получаем указатель но один и тот же обьект:
$ groovy Test.groovy
Invoke constructor
field=1
field=2
field=3
field=4
К плюсам такого подхода можно приписать отсутствие необходимости разработчику знать, что он работает с синглтоном.
Это же является и минусом.

Ирония в том, что Singleton это типичная ошибка проэктирования – анти-паттерн :)
Вот: http://code.google.com/p/google-singleton-detector/
Google даже написали Singleton Detector который находит синглетоны и помогает от них избавлятся.
По подробнее почитать про это можно тут: http://code.google.com/p/google-singleton-detector/wiki/WhySingletonsAreControversial
И тут:
http://c2.com/cgi/wiki?SingletonsAreEvil
Ну в этом вопросе уже на “вкус и цвет…”
Singleton это практически обход проблемы что нельзя создать сложный объект не описав его класс. В Scala для объектов специальное ключевое слово, и это правильно. В JavaScript тоже по сути можно сразу создавать такие объекты.
Так что они имеют точно такое же право на жизнь как например интерфейсы, которые частично можно заменить полностью абстрактными классами.
Но это ИМХО, в матчасть я особо не погружался.
За статью большое спасибо. Я не знаю Groovy но по моему можно было ещё применить волшебную аннотацию @Singleton
http://groovy.codehaus.org/api/groovy/lang/Singleton.html
в 2007м когда писалась эта заметка – аннотаций в Groovy вообще небыло :)