Ce sujet a produit qlqs threads par le passé
AS3 - Singleton et solution plus mieux
http://groups.google.com/group/FCNG/msg/a2c66e1572c53a35
singleton et MXML
http://groups.google.com/group/FCNG/msg/e97f3613c7c666b3
Singleton
http://groups.google.com/group/FCNG/msg/c204f1caebdfc131
et apres avoir lu
Singleton Considered Stupid
http://steve.yegge.googlepages.com/singleton-considered-stupid
il faut absoluement que je corrige ce que j'ai dit par le passé sur
les singletons
donc on reprends
----
http://fr.wikipedia.org/wiki/Singleton_(Motif_de_conception)
" Dans un langage à base de prototypes, où sont utilisés des objets
mais pas
des classes, un singleton désigne seulement un objet qui n'a pas de
copies,
et qui n'est pas utilisé comme prototype pour d'autres objets. "
----
ok et moi j'avais interpreté ca comme ca
----
le but d'un Singleton:
- crée une instance unique de la classe
- avoir un acces global a cet instance
- ne pas pouvoir instancier la classe plus d'une fois
----
voilà le probleme est ici, le singleton est oui ok une instance
unique,
mais pas pour ne pas pouvoir instancier la class plus d'une fois,
ca c'est faux.
et c'est ce que Steve Y. décrit en Java qui m'a fait réaliser ca
----
It was a little more boilerplate code per Singleton, because I had to
declare a public static version of each method and delegate to a
private instance method, BUT, my client code then turned into this:
RegistrationManager.registerUser(...);
Whoo! My client code (by which I mean anyone in the same process using
the Singleton instance) no longer needed to do that extra
getInstance() call, which was just plain annoying and useless.
But why stop there? Why not just get rid of the delegation altogether?
I resolved to figure this out, and hopped on the patterns newsgroups
where Ralph Johnson lurked. Lo and behold, Ralph had answered that
very question the month before. His answer: it doesn't matter; using
static methods is fine - that's still a Singleton.
Great! Gosh, I was excited. I'd get rid of the Singleton instance (and
the boilerplate), make everything static, and my code was now super
clean.
Yep. I was using classes purely as namespaces.
Since in Java, classes are the ONLY globally available namespace
mechanism built into the language, this is actually a common thing to
do, Singleton or no. Unless you want to build your own reflective
registry, or use JNDI or something, classes are your only option for
separating your code into namespaces. So in that regard, the (very)
little I was doing with classes wasn't actually so wrong.
----
oui voila pourquoi les gens utilisent getInstance()
leur class est le namespace, et dans ce namespace
il n'ont trouvé que getInstance() pour initialiser la class DANS le
namespace
mon path de singleton: foobar.MySingleton
mon acces a l'instance: foobar.MySingleton.getInstance()
mais ca c'est pour Java bordel de merde,
c'est une déficience du language Java que le language AS3 n'a pas
en AS3 on peut définir des variables au niveau du package
package foobar
{
public const MySingleton:BLAH = new BLAH();
}
donc ce que j'écrivais là
------
package test
{
public const Singleton:_Singleton = new _Singleton();
}
internal class _Singleton
{
function _Singleton()
{
}
public const testconst:String = "hello world";
public var testvar:String = "bonjour le monde";
public function testMethod():String
{
return "ni hao shijie";
}
}
------
ok ca marche, mais avec la correction
qu'on a pas besoin de déclarer la class _Singleton dans le meme
fichier,
on peut tres bien l'avoir dans un fichier à part (et en fait c'est
meme conseillé
vu que le internal namespace déclaré en dehors du package est partagé
et peut créer des collisions).
donc voilà, non pas besoin de getInstance() de merde, pas besoin de
constructor private,
juste besoin d'utiliser les features de base de AS3, cad
une constante déclarée au niveau du package, cela nous donnera une
point d'acces unique et global
à une instance unique (const).
test/Singleton.as
------
package test
{
public const Singleton:_Singleton = new _Singleton();
}
------
test/_Singleton.as
------
package test
{
[ExcludeClass]
internal class _Singleton
{
function _Singleton()
{
}
public const testconst:String = "hello world";
public var testvar:String = "bonjour le monde";
public function testMethod():String
{
return "ni hao shijie";
}
}
}
------
note: _Singleton ou autre convention de nommage comme SingletonImpl,
etc.
le but est de gardé test.Singleton comme point d'acces public
et la class d'implementation "cachée" par le namespace internal
et en option si vous voullez encore plus "cacher" cette class et bien
utilisez les namespaces
test/Singleton.as
------
package test
{
public const Singleton:_Singleton = new _Singleton( "secret" );
}
------
test/_Singleton.as
------
package test
{
[ExcludeClass]
internal class _Singleton
{
private var ns:Namespace;
private namespace unlock;
private var _testvar:String = "bonjour le monde";
public function _Singleton( key:String = "" )
{
//ns = unlock;
if( key == "secret" )
{
ns = unlock;
}
ctor();
}
public function ctor():void
{
if( ns == null )
{
return;
}
ns::ctor();
}
unlock function ctor():void
{
//constructor instanciation here;
trace( "_Singleton ctor" );
}
public function get testvar():String
{
return "";
}
unlock function get testvar():String
{
return _testvar;
}
public function testMethod():String
{
if( ns == null )
{
return "";
}
return ns::testMethod();
}
unlock function testMethod():String
{
return "ni hao shijie";
}
}
}
------
parce que votre declaration public utilise une clef "secret"
qui ouvre l'usage d'un namespace interne
test.Singleton.testMethod(); marchera
qlqn qui initialiserait la class sans ce namespace
package test
{
public var Singleton2:_Singleton = new _Singleton();
}
quand il fera un appel sur
test.Singleton.testMethod();
n'obtiendra rien
et pour le coup du ctor _Singleton qui n'est pas private
et bien le namespace gere ca aussi
mais dans le principe l'usage des namespaces dans ce cas là
est extreme et serait presque dans tous les cas inutile
(sauf cas d'une instanciation dynamique de ce singleton ;))
bref, le truc a retenir
c'est une instance unique que l'on veut,
l'instanciation de la class est secondaire
c'est l'acces global de cette instance unique qui constitue le
singleton
donc je me corrige
"l'avantage du package c est qu on peut forcer
la class a etre internal pour qu elle ne soit pas
instanciée plus d une fois (si on a besoin de ce genre de feature). "
-> ce n'est pas le vrai probleme
le vrai probleme c'est que les gens confondent instanciation unique
et acces unique, ils appliquent une logique Java qui ne permet que
d'utiliser une class pour definir un namespace à un language AS3
qui peut faire ca bien plus elegamment.
avoir une const au niveau du package est bien plus puissant
que faire des check interne dans le ctor d'une class pour s'assurer
que l'instance est unique
si je reprends le code du dessus
test.Singleton est unique
l'acces est unique
l'instance est unique
alors oui on peut me sortir, mais si je me met dans le meme package
"test"
je peux definir une autre var ou const qui instanciera à nouveau
_Singleton
ok...
mais cela n'empeche pas que test.Singleton reste unique et inchangé,
on ne peut pas overrider test.Singleton
donc le seul probleme qu'il reste c'est la multiple instanciation
d'une class
qu'on prefererait garder instanciable une seule fois
ce n'est pas toujours important pour le singleton
(on peut utliser des namespaces comme definit en haut, ou on peut
aller
dans le radical est definir la class en dehors du package, qlqch que
je deconseille
en general mais bon...)
j'aime bien wikipedia mais meme eux (enfin ceux qui ont ecris la page)
se basent bcp trop sur le concept de Java
http://en.wikipedia.org/wiki/Singleton_pattern
"In software engineering, the singleton pattern is a design pattern
that is used to restrict instantiation of a class to one object. This
is useful when exactly one object is needed to coordinate actions
across the system."
ici l'important c'est le "one object", pas vraiment que la class ne
soit instanciée qu'une seule fois
alors bien sur si je suis en Java et que mon acces de singleton est
test.Singleton
bah oui bien sur j'ai pas trop d'autre choix de faire
test.Singleton.getInstance()
car j'utilise un namespace de class pour definir ce singleton
mais dans notre cas en AS3 c'est la constante qui definit le
singleton,
pas la class utilisée pour l'instancier.
encore wikipedia
"Implementation of a singleton pattern must satisfy the single
instance and global access principles. It requires a mechanism to
access the singleton class member without creating a class object and
a mechanism to persist the value of class members among class
objects."
utiliser notre const en AS3 respecte tout à fait cela
oui c'est bien une instance unique et elle a bien un acces unique
là où la confusion commence c'est quand la definition de
l'implementation continue
"The singleton pattern is implemented by creating a class with a
method that creates a new instance of the class if one does not exist.
If an instance already exists, it simply returns a reference to that
object. To make sure that the object cannot be instantiated any other
way, the constructor is made protected (not private, because reuse and
unit test could need to access the constructor). Note the distinction
between a simple static instance of a class and a singleton: although
a singleton can be implemented as a static instance, it can also be
lazily constructed, requiring no memory or resources until needed.
Another notable difference is that static member classes cannot
implement an interface, unless that interface is simply a marker. So
if the class has to realize a contract expressed by an interface, it
really has to be a singleton."
oui mais AS3 ca tourne dans une VM
donc meme si j'ai un
package test
{
public const Singleton:_Singleton = new _Singleton();
}
et que autre part je fais un
import test.Singleton;
si je n'accede pas de propriétés sur ce singleton, il n'est pas
instancié
preuve
---
309 AVMINF: MTHD global$init ()
309 AVMINF: MTHD flash.system::ApplicationDomain$cinit ()
309 AVMINF: MTHD global$init ()
309 AVMINF: MTHD flash.display::LoaderInfo$cinit ()
309 AVMINF: MTHD flash.events::EventDispatcher () @ 0x198442F0
309 AVMINF: MTHD flash.display::LoaderInfo () @ 0x19844350
309 AVMINF: MTHD flash.events::EventDispatcher () @ 0x198442F0
---
si j'accede juste une prop/methode de ce singleton
alors là oui le singleton est instancié
---
475 AVMINF: MTHD global$init ()
476 AVMINF: MTHD global$init ()
476 AVMINF: MTHD global$init ()
476 AVMINF: MTHD test::_Singleton$cinit ()
477 AVMINF: MTHD test::_Singleton () @ 0x00FFD280
477 AVMINF: MTHD Object () @ 0x197C0B20
477 AVMINF: MTHD test::_Singleton/testMethod () @ 0x00FFD3D0
477 AVMINF: MTHD global/trace () @ 0x198442F0
ni hao shijie
478 AVMINF: MTHD global$init ()
478 AVMINF: MTHD flash.system::ApplicationDomain$cinit ()
478 AVMINF: MTHD global$init ()
478 AVMINF: MTHD flash.display::LoaderInfo$cinit ()
478 AVMINF: MTHD flash.events::EventDispatcher () @ 0x19844360
478 AVMINF: MTHD flash.display::LoaderInfo () @ 0x198443C0
478 AVMINF: MTHD flash.events::EventDispatcher () @ 0x19844360
---
donc a moins d'avoir bcp de choses qui s'execute dans le ctor du
singleton
on se fout un peu du lazy init
voir aussi
http://www.onflex.org/ACDS/AS3TuningInsideAVM2JIT.pdf
"- Initialization functions ($init, $cinit) are interpreted
- Everything else is JIT "
donc au pire, vous faites votre lazy init vous meme, par ex:
-----
package test
{
internal class _Singleton
{
private var lazy:Boolean;
public function _Singleton()
{
lazy = true;
}
private function _ctor():void
{
//a lot of stuff to set up
lazy = false;
}
public function get someVar():String
{
if( lazy )
{
_ctor();
}
return _someVar;
}
}
}
-----
si vous ne mettez rien dans le ctor du singleton
476 AVMINF: MTHD test::_Singleton$cinit ()
c'est peanuts, on s'en fout qu'il soit instancié ou pas
apres bien sur vous avez different cas, le lazy init
pour ne pas construire la logique interne su singleton si vous n'en
avez pas besoin
mais aussi l'inverse, etre sur que le singleton est instancié avant un
appel de class
le derniere point et bah en fait le compilo s'en charge :)
le seul truc a savoir c est que $init() et $cinit() s'executent en
premier
donc si vous voulez votre singleton instancié avant un autre
déclaré jsute un
private var _blah1:* = Singleton1;
private var _blah2:* = Singleton2;
à l'initializatino de votre appli, comme ca vous forcerez l'ordre
d'appel du $cinit
bref, pour finir j'ai édité
http://en.wikipedia.org/wiki/Singleton_pattern#Actionscript_3.0
petit conseil, si vous choisissez de definir la class en dehors du
package
utilisez un nom vraiment unique, genre _Singleton_0x00FFD280
maintenant pour revenir sur pourquoi le singleton peut etre stupide
qui d'ailleurs a qlqs raisons listées ici
The Singleton
http://www.flashbrighton.org/wordpress/?p=91
alors listons tout, et comparons avec l'usage d'une const pour notre
singleton
a) lazy instanciation
"the lazy instantiation means that the instance is not created unless
it is needed, saving memory wastage. Now this presents no problems in
AS (as it is single-threaded), but in a multi-threaded environment,
the instantiation must be synchronised to prevent multiple instances
being created by different threads."
bon ca ne couvre pas completement tous les cas
on peut faire une lazy instanciation facilement (cf au dessus)
cad pouvoir referencer un membre de notre singleton mais sans pour
autant
avoir le gros code instancié, mais seulement instancié quand on accede
effectivement
a ce membre, bon ok, en fait par defaut on a meme pas besoin de faire
ca, comme dis
ci-dessus a pas de multi-thread, oui mais y a de multile
ApplicationDomain
et là on pourrait avoir un probleme si 2 domain different voullaient
utiliser le meme singleton.
b) pas de ctor private
"in AS, a constructor can not be private, so the above example is open
to misuse, as the constructor can still be accessed by any client. It
is not properly a Singleton yet, so some form of validation must be
required to instantiate it."
aller on va pleurer..bah non, on s'en fout
1) la const est unique et restera unique quoi qu'il arrive
donc meme si le ctor peut etre instancié ailleurs on ne perds pas
notre singleton.
Le seul probleme c'est si on veut garder l'instanciation unique
parce que
on initialize des gros trucs, comme acces a une BDD avec du cache
etc.
2) rien que d'avoir une class internal ca veut dire que si un autre
dev
instancie un doublon du singleton bah il l'aura bien cherché et
bref
faudra pas qu'il vienne pleurer que ca marche plus.
c'est comme le mx_internals, oui possible a utiliser mais si tu le
fais
tu te demerdes on est pas responssable.
3) la declaration en dehors du package.
C'est extreme certes et voir peut creer des problemes au niveau
des noms
dans ce package "special" mais là impossible de reinstancier la
meme class ailleurs.
continuons sur les singleton anti-pattern
c) une instance unique d'une classe n'est pas forcément un singleton
"just because one system needs only one instance of a certain class,
doesn’t mean that another one does also. If this is the case, then the
class should not be a Singleton, and to do so, is really just using a
Singleton as a glorified global variable (GlobalVariablesAreBad).
There will be a better way for a client to obtain the instance
required."
pas faux, mais je reprends le cas du ApplicationDomain, j'ai pas
d'exemple sous la main
mais cela pourrait illustre un cas où on voudrait plus d'une instance
du singleton mais
que cette instance soit unique par domain.
(si qlqn a un exemple je suis preneur)
d) l'usage des methodes statiques empeche d'implementer une interface
pour la definition du singleton
"because a Singleton manages its instance through a static method:
1. sub-classing is possible, but fraught with complications
2. it can not implement an interface
3. all references must be hard-coded with the name of the class,
promoting (as b does) tight coupling between classes
"
pas dans notre cas
- on peut faire du sub-classing dans le meme package
- on peut implementer une interface
apres le coup d'ecrire en dur le nom d'une class ou d'une constante
dans notre cas,
dans une vraie logique de singleton ca se justifie -> un seul et
unique point d'acces
donc oui forcément y a du coupling.
e) pas de polymorphisme
"therefore, Singletons can not be polymorphic"
bah si, si au lieu de renvoyer le type _SIngleton dans notre const
on renvoit une interface ISingleton et que _Singleton implemente cette
interface
on a du polymorphisme
d) SRP
"a Singleton manages its own instantiation, but it also manages its
own business logic. This violates the Single Responsibility
Principle."
ok, vrai.
e) etat persistant
"a Singleton’s state persists as long as the application is running -
this is very bad for unit testing, as it out-lives any unit that is
being tested (apparently)."
oui c'est vrai c'est pas terrible à tester, mais si je veux un
singleton sans "state"
bah ca ne sert a rien de creer une singleton, une classe statique
suffit.
merci Steve Y., juste ca
"Yep. I was using classes purely as namespaces."
ca m'a fait réalisé où était le probleme
zwetan
ps: une petite liste de pleins de blogs qui se prennent la tete sur le
mauvais probleme
(ah bah oui qd on pense en Java mais qu'on ecrit de l'AS3 ca peut
confusioner)
ActionScript 3 Singleton Redux
http://www.darronschall.com/weblog/archives/000274.cfm
Singleton Pattern in AS3
http://life.neophi.com/danielr/2006/10/singleton_pattern_in_as3.html
Singleton in AS3
http://www.cynergysystems.com/blogs/page/andrewtrice?entry=singleton_in_as3
("we are the leaders in ria development" --> désolé mais ARF!)
AS3: Singletons
http://www.gskinner.com/blog/archives/2006/07/as3_singletons.html
(et oui meme lui)
Singleton in ActionScript 3.0
http://skovalyov.blogspot.com/2006/12/there-are-some-ways-to-create-singleton.html
(pas loin mais pas encore ca)
Singleton Pattern in Cairngorm 2.1 with Actionscript 3
http://tomschober.blogspot.com/2007/01/singleton-pattern-in-cairngorm-21-with.html
(forcement si ils suivent la meme logique poru tous les pattern, bah
ouais on s'etonne pas du resultat)
http://manishjethani.com/blog/2008/04/09/how-i-do-singletons-in-as3/
(exemple parfait de la confusion, il cree sa class singleton en
internal mais il utilise toujours getInstance()!)