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

К плюсам такого подхода можно приписать отсутствие необходимости разработчику знать, что он работает с синглтоном.
Это же является и минусом.

Комментариев: 4

4 Responses to “Easy Singleton in Groovy”

Комментарии:

  1. Vladimir Frolov

    Ирония в том, что 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

  2. vadim

    Ну в этом вопросе уже на “вкус и цвет…”

  3. stokito

    Singleton это практически обход проблемы что нельзя создать сложный объект не описав его класс. В Scala для объектов специальное ключевое слово, и это правильно. В JavaScript тоже по сути можно сразу создавать такие объекты.
    Так что они имеют точно такое же право на жизнь как например интерфейсы, которые частично можно заменить полностью абстрактными классами.
    Но это ИМХО, в матчасть я особо не погружался.

    За статью большое спасибо. Я не знаю Groovy но по моему можно было ещё применить волшебную аннотацию @Singleton
    http://groovy.codehaus.org/api/groovy/lang/Singleton.html

  4. Vadim Voituk

    в 2007м когда писалась эта заметка – аннотаций в Groovy вообще небыло :)

Leave a Reply