There is a pattern that I use from time to time, but I'm not quite sure what it is called. I was hoping that the SO community could help me out.
The pattern is pretty simple, and consists of two parts:
A factory method that creates objects based on the arguments passed in.
Objects created by the factory.
So far this is just a standard "factory" pattern.
The issue that I'm asking about, however, is that the parent in this case maintains a set of references to every child object that it ever creates, held within a dictionary. These references can sometimes be strong references and sometimes weak references, but it can always reference any object that it has ever created.
When receiving a request for a "new" object, the parent first searches the dictionary to see if an object with the required arguments already exists. If it does, it returns that object, if not, it returns a new object and also stores a reference to the new object within the dictionary.
This pattern prevents having duplicative objects representing the same underlying "thing". This is useful where the created objects are relatively expensive. It can also be useful where these objects perform event handling or messaging - having one object per item being represented can prevent multiple messages/events for a single underlying source.
There are probably other reasons to use this pattern, but this is where I've found this useful.
My question is: what to call this?
In a sense, each object is a singleton, at least with respect to the data it contains. Each is unique. But there are multiple instances of this class, however, so it's not at all a true singleton.
In my own personal terminology, I tend to call the parent class a "global singleton". I then call the created objects "local singletons". I sometimes also say that the created objects have "reference equality", meaning that if two variables reference the same data (the same underlying item) then the reference they each hold must be to the same exact object, hence "reference equality".
But these are my own invented terms, and I am not sure that they are good ones.
Is there standard terminology for this concept? And if not, could some naming suggestions be made?
Thanks in advance...
Update #1:
In Mark Seemans' reply, below, he gives the opinion that "The structure you describe is essentially a DI Container used as a Static Service Locator (which I consider an anti-pattern)."
While I agree that there are some similarities, and Mark's article is truly excellent, I think that this Dependency Injection Container / Static Service Locator pattern is actually a narrower implementation of the general pattern that I am describing.
In the pattern described in the article, the service (the 'Locator' class) is static, and therefore requires injection to have variability in its functionality. In the pattern I am describing, the service class need not be static at all. One could provide a static wrapper, if one wants, but being a static class is not at all required, and without a static class, dependency injection is not needed (and, in my case, not used).
In my case the 'Service' is either an interface or an abstract class, but I don't think that 'Service' and 'Client' classes are even required to be defined for the pattern I am describing. It is convenient to do so, but if all the code is internal, the Server class could simply be a 'Parent' class that controls the creation of all children via a factory method and keeps weak (or possibly strong) references to all of its children. No injection, nothing static, and not even a requirement to have defined interfaces or abstract classes.
So my pattern is really not a 'Static Service Locator' and neither is it a 'Dependency Injection Container'.
I think the pattern I'm describing is much more broad than that. So the question remains: can anyone identify the name for this approach? If not, then any ideas for what to call this are welcome!
Update #2:
Ok, it looks like Gabriel Ščerbák got it with the GoF "Flyweight" design pattern. Here are some articles on it:
- Flyweight pattern (Wikipedia)
- Flyweight design pattern (dofactory.com)
- Flyweight Design Pattern (sourcemaking.com)
A 'flyweight factory' (server) and 'flyweight objects' (client) approach using interfaces or abstract classes is well explained in the dofactory.com article an is exactly what I was trying to explain here.
The Java example given in the Wikipedia article is the approach I take when implementing this approach internally.
The Flyweight pattern also seems to be very similar to the concept of hash consing and the Multiton pattern.
Slightly more distantly related would be an object pool, which differs in that it would tend to pre-create and/or hold on to created objects even beyond their usage to avoid the creation & setup time.
Thanks all very much, and thanks especially to Gabriel.
However, if anyone has any thoughts on what to call these child objects, I'm open to suggestions. "Internally-Mapped children"? "Recyclable objects"? All suggestions are welcome!
Update #3:
This is in reply to TrueWill, who wrote:
The reason this is an anti-pattern is because you are not using DI. Any class that consumes the Singleton factory (aka service locator) is tightly coupled to the factory's implementation. As with the new keyword, the consumer class's dependencies on the services provided by the factory are not explicit (cannot be determined from the public interface). The pain comes in when you try to unit test consumer classes in isolation and need to mock/fake/stub service implementations. Another pain point is if you need multiple caches (say one per session or thread)
Ok, so Mark, below, said that I was using DI/IoC. Mark called this an anti-pattern.
TrueWill agreed with Mark's assessment that I was using DI/IoC in his comment within Mark's reply: "A big +1. I was thinking the same thing - the OP rolled his own DI/IoC Container."
But in his comment here, TrueWill states that I am not using DI and it is for this reason that it is an anti-pattern.
This would seem to mean that this is an anti-pattern whether using DI or not...
I think there is some confusion going on, for which I should apologize. For starters, my question begins by talking about using a singleton. This alone is an anti-pattern. This succeeded in confusing the issue with respect to the pattern I am trying to achieve by implying a false requirement. A singleton parent is not required, so I apologize for that implication.
See my "Update #1" section, above, for a clarification. See also the "Update #2" section discussing the Flyweight pattern that represents the pattern that I was trying to describe.
Now, the Flyweight pattern might be an anti-pattern. I don't think that it is, but that could be discussed. But, Mark and TrueWill, I promise you that in no way am I using DI/IoC, honest, nor was I trying to imply that DI/IoC is a requirement for this pattern.
Really sorry for any confusion.