JaroslavTulach: /* Testability */ - 2021-10-12 07:25:05

Testability

←Older revision Revision as of 07:25, 12 October 2021
Line 67: Line 67:
== [[Testability]] ==
== [[Testability]] ==
-
Of course, these days one just has to write code that is inherently testable. One of the critiques of [[singleton]] pattern was that it does not allow this. That it is not possible to ''mock'' in different [[singleton]] behavior during test execution. [[Injectable Singleton]]s are here to allow you to do exactly this:
+
Of course, these days one just has to write code that is inherently testable. Critics of regular [[singleton]] pattern accuse singletons from not being testable. That it is not possible to ''mock'' in different [[singleton]] behavior during test execution. [[Injectable Singleton]]s are here to allow you to do exactly this:
<source lang="java" snippet="singletons.injectable.test"/>
<source lang="java" snippet="singletons.injectable.test"/>

JaroslavTulach: /* Usage */ - 2021-10-12 07:22:57

Usage

←Older revision Revision as of 07:22, 12 October 2021
Line 37: Line 37:
<source lang="java" snippet="singletons.injectable.usage"/>
<source lang="java" snippet="singletons.injectable.usage"/>
-
This makes the whole use as simple as possible by following the first half of the [[Convention over Configuration]] motto: ''If you are OK with default implementation, just use it''. As soon as a client code links with the [[API]] class, it can be sure that it will get reasonable implementation. No other configuration is needed. This is the output of such sample execution (which includes just the client code and the [singleton]]'s [[API]] with default implementation):
+
This makes the whole use as simple as possible by following the first half of the [[Convention over Configuration]] motto: ''If you are OK with default implementation, just use it''. As soon as a client code links with the [[API]] class, it can be sure that it will get reasonable implementation. No other configuration is needed. This is the output of such sample execution (which includes just the client code and the [[singleton]]'s [[API]] with default implementation):
<source lang="bash">
<source lang="bash">

JaroslavTulach: /* Polemic */ - 2010-02-09 20:01:23

Polemic

←Older revision Revision as of 20:01, 9 February 2010
Line 97: Line 97:
-
[[Category:APIDesignPatterns]] [[APIDesignPatterns:Creational]] [[APIDesignPatterns:Meta]]
+
[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Creational]] [[Category:APIDesignPatterns:Meta]]

JaroslavTulach at 19:58, 9 February 2010 - 2010-02-09 19:58:16

(Difference between revisions)

JaroslavTulach: /* Configure */ - 2010-02-09 19:31:13

Configure

←Older revision Revision as of 19:31, 9 February 2010
Line 54: Line 54:
<source lang="bash">
<source lang="bash">
-
$ java -jar DisplayerMain.jar -cp Displayer.jar:DefaultDisplayerImpl.jar
+
$ java -jar DisplayerMain.jar -cp Displayer.jar:SwingDisplayerImpl.jar
</source>
</source>

JaroslavTulach: /* Configure */ - 2010-02-09 19:30:39

Configure

←Older revision Revision as of 19:30, 9 February 2010
Line 47: Line 47:
== Configure ==
== Configure ==
-
Of course, using the dummy implementation is not always optimal. As our systems get more and more [[Modularity|assembled at deploy time]] there especially needs to be a way to support easy configuration during application deployment. This is inherently supported due to adherence to [[ServiceLoader|Java Standard Extension Mechanism]]. Any time later, in any additional project that produces completely independent [[JAR]] file one can compile following piece of code:
+
Of course, using the dummy implementation is not always optimal. As our systems get more and more [[Modularity|assembled at deploy time]] there especially needs to be a way to support easy configuration during application deployment. This is inherently supported due to adherence to [[ServiceLoader|Java Standard Extension Mechanism]]. Any time later, while producing completely different [[JAR]] file, one can compile following piece of code:
<source lang="java" snippet="singletons.injectable.implement"/>
<source lang="java" snippet="singletons.injectable.implement"/>
-
The [[JAR]] containing this class (and the generated ''META-INF/services/'' entry, as generated by processing ''@ServiceProvider'' annotation via compile time [[AnnotationProcessor]] or alternatively created manually) can be put onto the application classpath. As soon as you do
+
The [[JAR]] containing this class (and the associated ''META-INF/services/'' entry, as generated by processing ''@ServiceProvider'' annotation via compile time [[AnnotationProcessor]] or alternatively created manually) can be put onto the application classpath. As soon as you do
<source lang="bash">
<source lang="bash">

JaroslavTulach: /* Defining the Singleton */ - 2010-02-09 19:26:46

Defining the Singleton

←Older revision Revision as of 19:26, 9 February 2010
Line 21: Line 21:
In this case, the implementation of the '''yesOrNo''' method is really trivial and not very useful, but often there are cases when the default behavior can be made acceptable for many purposes.
In this case, the implementation of the '''yesOrNo''' method is really trivial and not very useful, but often there are cases when the default behavior can be made acceptable for many purposes.
-
Now the time as come to write proper initialization code (that will support [[Component Injection|extensibility]]). We can do it either in [[Java]]6 standard way via [[ServiceLoader]]:
+
Now the time has come to write proper initialization code (that will support [[Component Injection|extensibility]]). We can do it either in [[Java]]6 standard way via [[ServiceLoader]]:
<source lang="java" snippet="singletons.injectable.serviceloader"/>
<source lang="java" snippet="singletons.injectable.serviceloader"/>

JaroslavTulach: /* Polemic */ - 2010-02-08 21:35:15

Polemic

←Older revision Revision as of 21:35, 8 February 2010
Line 87: Line 87:
Right, the [[Injectable Singleton]]s as presented so far, are violating all the suggestions to [[Separate APIs for Clients and Providers]]. This may or may not matter. It is probably OK to use subclassable abstract classes for [[API]] that is from 99% called and only rarely implemented. This is often cause of [[singleton]]s - there is one instance and many callers. Thus violating the best practice may be acceptable (in [[NetBeans]] we have a lot of such [[singleton]]'s, ''WindowManager'', ''DialogDisplayer'', ''ExecutionEngine'' and we never faced much problems due to that - there are usually just three implementations of those - one default, one real, one for tests).
Right, the [[Injectable Singleton]]s as presented so far, are violating all the suggestions to [[Separate APIs for Clients and Providers]]. This may or may not matter. It is probably OK to use subclassable abstract classes for [[API]] that is from 99% called and only rarely implemented. This is often cause of [[singleton]]s - there is one instance and many callers. Thus violating the best practice may be acceptable (in [[NetBeans]] we have a lot of such [[singleton]]'s, ''WindowManager'', ''DialogDisplayer'', ''ExecutionEngine'' and we never faced much problems due to that - there are usually just three implementations of those - one default, one real, one for tests).
-
In case the dual nature of these [[Injectable Singleton]]s shall be a problem, the cure is simple. Just make all their methods ''protected abstract'' (as advocated in [[ClarityOfAccessModifiers]]). Then it will be clear that the [[singleton]] is here to be [[injection|injected]] and not called (no [[API]] client can call ''protected'' methods). Then, of course, you need to provide some ''public'' methods that [[ClientAPI]] users can call. That is more verbose, but it clearly separates the concerns: There is a well defined [[API]] for clients as well as providers.
+
In case the dual nature of these [[Injectable Singleton]]s shall be a problem, the cure is simple. Just make all their methods ''protected abstract'' (as advocated in [[ClarityOfAccessModifiers]]). Then it will be clear that the [[singleton]] is here to be [[injection|injected]] and not called (no [[API]] client can call ''protected'' methods). Then, of course, you need to provide some other ''public'' methods that [[ClientAPI]] users can call. That is more verbose, but it clearly separates the concerns: There is a well defined [[API]] for clients as well as providers.
<comments/>
<comments/>

JaroslavTulach: /* Polemic */ - 2010-02-08 21:33:09

Polemic

←Older revision Revision as of 21:33, 8 February 2010
Line 85: Line 85:
One of the critics mentioned that this is not really a [[singleton]], as it allows multiple instances to be created. Yes, people can subclass the abstract class and create as many instances as they want. However whoever uses just the static [[API]] getter, is guaranteed to receive only one, shared instance. For those who use the [[ClientAPI]] part of such [[singleton]], the whole things appears to be [[singleton]]. That is why this worry is more about ''beauty'' than real world problems.
One of the critics mentioned that this is not really a [[singleton]], as it allows multiple instances to be created. Yes, people can subclass the abstract class and create as many instances as they want. However whoever uses just the static [[API]] getter, is guaranteed to receive only one, shared instance. For those who use the [[ClientAPI]] part of such [[singleton]], the whole things appears to be [[singleton]]. That is why this worry is more about ''beauty'' than real world problems.
-
TBD:
+
Right, the [[Injectable Singleton]]s as presented so far, are violating all the suggestions to [[Separate APIs for Clients and Providers]]. This may or may not matter. It is probably OK to use subclassable abstract classes for [[API]] that is from 99% called and only rarely implemented. This is often cause of [[singleton]]s - there is one instance and many callers. Thus violating the best practice may be acceptable (in [[NetBeans]] we have a lot of such [[singleton]]'s, ''WindowManager'', ''DialogDisplayer'', ''ExecutionEngine'' and we never faced much problems due to that - there are usually just three implementations of those - one default, one real, one for tests).
-
* [[Separate APIs for Clients and Providers]]
+
 
 +
In case the dual nature of these [[Injectable Singleton]]s shall be a problem, the cure is simple. Just make all their methods ''protected abstract'' (as advocated in [[ClarityOfAccessModifiers]]). Then it will be clear that the [[singleton]] is here to be [[injection|injected]] and not called (no [[API]] client can call ''protected'' methods). Then, of course, you need to provide some ''public'' methods that [[ClientAPI]] users can call. That is more verbose, but it clearly separates the concerns: There is a well defined [[API]] for clients as well as providers.
 +
 
 +
 
 +
<comments/>

JaroslavTulach: /* Injectable Meta-Singleton */ - 2010-02-08 21:19:34

Injectable Meta-Singleton

←Older revision Revision as of 21:19, 8 February 2010
Line 77: Line 77:
As mentioned earlier, there are two recommended ways to configure the [[Injectable Singleton]]s. You can rely on [[Java]]'s standard [[ServiceLoader]] or you can choose [[Lookup]] library. Of course, it is often preferable to have as little dependencies as possible (and thus the [[ServiceLoader]] shall be natural choice), however the [[Lookup]] library offers certain advantages that make it attractive as well. Most of them are listed [[Lookup|elsewhere]], but let me point your attention to one, relevant for topic of this page.
As mentioned earlier, there are two recommended ways to configure the [[Injectable Singleton]]s. You can rely on [[Java]]'s standard [[ServiceLoader]] or you can choose [[Lookup]] library. Of course, it is often preferable to have as little dependencies as possible (and thus the [[ServiceLoader]] shall be natural choice), however the [[Lookup]] library offers certain advantages that make it attractive as well. Most of them are listed [[Lookup|elsewhere]], but let me point your attention to one, relevant for topic of this page.
-
[[Lookup]] is a [[singleton]]. In fact it is an [[Injectable singleton]]! There is a ''Lookup.getDefault()'' method which returns a ''meta''-factory for all [[Injectable Singleton]]s, and this factory is also [[injection|injectable]]. As a result (in contrary to [[ServiceLoader]]) one can completely customize the way all [[Injectable singleton]]s in a system are discovered and created.
+
[[Lookup]] is a [[singleton]]. In fact it is an [[Injectable singleton]]! There is a ''Lookup.getDefault()'' method which returns a ''meta''-factory for all [[Injectable Singleton]]s, and this factory is also [[injection|injectable]]. As a result (in contrary to [[ServiceLoader]] where the default behavior is hard-coded and cannot be [[injection|injected]]) one can completely customize the way all [[Injectable singleton]]s in a system are discovered and created.
One can even use [[LookupAndSpring]] bridge, register [[Lookup]] that wraps a [[Spring]]'s [[XML]] configuration file as the default meta-[[singleton]]. Then one can configure the whole application using classical [[Dependency Injection]] tricks (I can provide an executable demo, if there is an interest; just [[talk:Injectable Singleton|leave a note]]).
One can even use [[LookupAndSpring]] bridge, register [[Lookup]] that wraps a [[Spring]]'s [[XML]] configuration file as the default meta-[[singleton]]. Then one can configure the whole application using classical [[Dependency Injection]] tricks (I can provide an executable demo, if there is an interest; just [[talk:Injectable Singleton|leave a note]]).