模式:本地单例与全局单例?
我有时会使用一种模式,但我不太确定它叫什么。我希望 SO 社区能够帮助我。
该模式非常简单,由两部分组成:
根据传入的参数创建对象的工厂方法。
创建的对象由工厂创建。
到目前为止,这只是一个标准的“工厂”模式。
然而,我要问的问题是,在这种情况下,父对象维护一组对其创建的每个子对象的引用,这些引用保存在字典中。这些引用有时可以是强引用,有时可以是弱引用,但它始终可以引用它曾经创建的任何对象。
当收到对“新”对象的请求时,父级首先搜索字典以查看具有所需参数的对象是否已存在。如果是,则返回该对象,如果不是,则返回一个新对象,并在字典中存储对新对象的引用。
这种模式可以防止重复的对象代表相同的底层“事物”。当创建的对象相对昂贵时,这非常有用。当这些对象执行事件处理或消息传递时,它也很有用 - 每一项表示一个对象可以防止单个底层源出现多个消息/事件。
使用此模式可能还有其他原因,但这就是我发现它有用的地方。
我的问题是:这个叫什么?
从某种意义上说,每个对象都是一个单例,至少就其包含的数据而言是这样。每一个都是独一无二的。但该类有多个实例,因此它根本不是真正的单例。
用我自己的个人术语来说,我倾向于将父类称为“全局单例”。然后我将创建的对象称为“本地单例”。我有时也说创建的对象具有“引用相等”,这意味着如果两个变量引用相同的数据(相同的基础项),那么它们各自持有的引用必须是同一个确切的对象,因此“引用相等”。
但这些都是我自己发明的术语,我不确定它们是否好用。
这个概念有标准术语吗?如果没有,可以提出一些命名建议吗?
提前致谢...
更新#1:
在 Mark Seemans 的回复中,下面,他给出了以下观点:“您描述的结构本质上是用作静态服务定位器的 DI 容器(我认为这是一种反模式)。”
虽然我同意存在一些相似之处,并且 Mark 的文章 确实非常出色,我认为这种依赖注入容器/静态服务定位器模式实际上是我所描述的一般模式的更窄的实现。
在本文描述的模式中,服务(“定位器”类)是静态的,因此需要注入才能使其功能具有可变性。在我描述的模式中,服务类根本不需要是静态的。如果需要的话,可以提供一个静态包装器,但根本不需要成为静态类,并且如果没有静态类,则不需要依赖项注入(并且在我的情况下,不使用依赖项注入)。
就我而言,“服务”要么是接口,要么是抽象类,但我认为甚至不需要为我描述的模式定义“服务”和“客户端”类。这样做很方便,但如果所有代码都是内部的,则服务器类可以只是一个“父”类,它通过工厂方法控制所有子级的创建,并保持对其所有子级的弱(或可能强)引用。孩子们。没有注入,没有静态,甚至不需要定义接口或抽象类。
所以我的模式实际上不是“静态服务定位器”,也不是“依赖注入容器”。
我认为我所描述的模式比这更广泛。所以问题仍然存在:任何人都可以确定这种方法的名称吗?如果没有,那么欢迎任何关于如何称呼它的想法!
更新#2:
好的,看起来像 Gabriel Ščerbák 通过 GoF“蝇量级”设计模式获得了它。以下是一些关于它的文章:
使用接口或抽象类的“享元工厂”(服务器)和“享元对象”(客户端)方法在 dofactory.com 文章正是我在这里试图解释的内容。
维基百科文章中给出的 Java 示例是我在内部实现此方法时采用的方法。
Flyweight 模式似乎也与 哈希 consing 和 Multiton 模式。
关系稍远一些的是对象池,它的不同之处在于它倾向于预先创建和/或保留创建的对象,甚至超出其使用范围,以避免创建和保存设置时间。
非常感谢大家,特别感谢加布里埃尔。
但是,如果有人对如何称呼这些子对象有任何想法,我愿意接受建议。 “内部映射的孩子”? “可回收物品”?欢迎所有建议!
更新#3:
这是对 TrueWill 的回复,他写道:
这是反模式的原因是因为您没有使用 DI。任何使用单例工厂(又名服务定位器)的类都与工厂的实现紧密耦合。与 new 关键字一样,消费者类对工厂提供的服务的依赖关系不是明确的(无法从公共接口确定)。当您尝试单独测试消费者类并且需要模拟/伪造/存根服务实现时,痛苦就会出现。另一个痛点是您是否需要多个缓存(例如每个会话或线程一个)
好的,下面的马克说我正在使用 DI/IoC。马克称这是一种反模式。
TrueWill 同意 Mark 的评估,即我在 Mark 的回复中的评论中使用了 DI/IoC:“一个大+1。我也在想同样的事情 - OP 推出了自己的 DI/IoC 容器。”
但 TrueWill 在他的评论中指出,我没有使用 DI,正是因为这个原因,它是一种反模式。
这似乎意味着无论是否使用 DI 这都是一种反模式......
我认为存在一些混乱,对此我应该道歉。首先,我的问题首先讨论使用单例。仅此一点就是一种反模式。这成功地将问题与我试图通过暗示错误的要求来实现的模式混淆了。不需要单亲父母,因此我对此表示歉意。
请参阅上面我的“更新#1”部分以获得澄清。另请参阅讨论代表我试图描述的模式的享元模式的“更新#2”部分。
现在,享元模式可能是一种反模式。我不认为是这样,但这可以讨论。但是,Mark 和 TrueWill,我向你们保证,我绝对不会使用 DI/IoC,老实说,我也没有试图暗示 DI/IoC 是此模式的要求。
真的很抱歉造成任何混乱。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
它看起来像 GoF 书中经典的 Flyweight 设计模式。它不包括使用哈希映射的单例工厂,但通常包括通过重用已创建的对象来节省空间和性能,该对象被许多其他对象引用。看看这个模式。
It looks like classic Flyweight design pattern from the GoF book. It does not cover the singleton factory with hash map, but generally covers saving on space and performance by reusing already created object, which is referenced by many other objects. Check out this pattern.
对象本身不遵循单例模式,因此将获取的对象称为单例可能会令人困惑。
回收工厂怎么样? :]
The objects themselves aren't following the singleton pattern, so maybe referring to the fetched objects as singleton can be confusing.
What about a Recycling Factory? :]
您描述的结构本质上是一个用作静态服务定位器的DI容器(其中我认为是反模式)。
此服务定位器创建的每个服务都有一个所谓的单例生命周期。大多数 DI 容器都支持此作为多个可用生命周期之一。
The structure you describe is essentially a DI Container used as a Static Service Locator (which I consider an anti-pattern).
Each of the services created by this Service Locator has a so-called Singleton lifetime. Most DI Containers support this as among several available lifetimes.