代码之家  ›  专栏  ›  技术社区  ›  Mike Rosenblum

模式:本地单例与全局单例?

  •  3
  • Mike Rosenblum  · 技术社区  · 14 年前

    我经常使用一种模式,但我不太确定它叫什么。我希望社会能帮助我。

    模式非常简单,由两部分组成:

    1. 根据传入的参数创建对象的工厂方法。

    2. 由工厂创建的对象。

    到目前为止,这只是一个标准的“工厂”模式。

    然而,我要问的问题是,在这种情况下,父对象维护了对它曾经创建的每个子对象的一组引用,保存在字典中。这些引用有时可以是强引用,有时是弱引用,但它始终可以引用它曾经创建的任何对象。

    当接收到“new”对象的请求时,父对象首先搜索字典以查看具有所需参数的对象是否已经存在。如果返回,则返回该对象;如果不返回,则返回新对象,并在字典中存储对新对象的引用。

    这种模式可以防止重复的对象代表相同的底层“物”。当创建的对象相对昂贵时,这很有用。在这些对象执行事件处理或消息传递的情况下,它也很有用——每个被表示的项目有一个对象可以防止单个基础源出现多个消息/事件。

    使用这个模式可能还有其他的原因,但这正是我发现它有用的地方。

    我的问题是:这叫什么?

    从某种意义上说,每个对象都是一个单例对象,至少就其包含的数据而言是这样。每一个都是独一无二的。但是这个类有多个实例,所以它根本不是真正的单例。

    在我个人的术语中,我倾向于称父类为“全局单例”。然后我将创建的对象称为“本地单例”。我有时也会说,创建的对象具有“引用相等”,也就是说,如果两个变量引用相同的数据(相同的基础项),那么它们各自持有的引用必须指向相同的精确对象,因此“引用相等”。

    但这些是我自己发明的术语,我不确定它们是否是好术语。

    这个概念有标准术语吗?如果没有,是否可以提出一些命名建议?

    事先谢谢…

    更新1:

    在下面马克·西曼的回答中,他认为 “您描述的结构本质上是用作静态服务定位器的DI容器(我认为是反模式的)。”

    虽然我同意有一些相似之处, Mark's article 是非常好的,我认为这个依赖注入容器/静态服务定位器模式实际上是我描述的一般模式的更窄的实现。

    在本文描述的模式中,服务(“locator”类)是静态的,因此需要注入在其功能上具有可变性。在我描述的模式中,服务类根本不需要是静态的。如果愿意的话,可以提供一个静态包装器,但是作为一个静态类根本不需要,如果没有静态类,就不需要依赖注入(在我的例子中,也不使用)。

    在我的例子中,“服务”不是接口就是抽象类,但我认为甚至不需要为我描述的模式定义“服务”和“客户机”类。这样做很方便,但是如果所有代码都是内部的,那么服务器类可能只是一个“父”类,它通过工厂方法控制所有子类的创建,并保持对所有子类的弱(或强)引用。没有注入,没有静态的,甚至没有定义接口或抽象类的需求。

    因此,我的模式实际上不是“静态服务定位器”,也不是“依赖注入容器”。

    我认为我所描述的模式比这要宽泛得多。所以问题仍然存在:是否有人能确定这种方法的名称?如果没有,那么欢迎有任何关于这个的想法!

    更新第2号:

    好吧,看起来像 Gabriel Ščerbák 用GOF的“飞锤”设计图案。下面是一些关于它的文章:

    使用接口或抽象类的“flyweight工厂”(服务器)和“flyweight对象”(客户端)方法在 dofactory.com article 这正是我想解释的。

    在Java中给出的示例 Wikipedia article 是我在内部实现此方法时采用的方法。

    飞锤图案似乎也非常类似于 hash consing 以及 Multiton pattern .

    稍微远一点的关系是 object pool 不同的是,它往往会预先创建和/或保留已创建的对象,甚至超出其使用范围,以避免创建和设置时间。

    非常感谢,特别感谢加布里埃尔。

    但是,如果有人对如何称呼这些儿童对象有任何想法,我愿意接受建议。”内部映射的子级“?”可回收物品?欢迎提出所有建议!

    更新3:

    这是对Truewill的回应,他写道:

    这是反模式的原因是因为您没有使用DI。任何使用单例工厂(即服务定位器)的类都与工厂的实现紧密耦合。与new关键字一样,使用者类对工厂提供的服务的依赖性不是显式的(无法从公共接口确定)。当您试图独立地对使用者类进行单元测试,并且需要模拟/伪造/存根服务实现时,就会遇到麻烦。另一个痛点是如果您需要多个缓存(比如每个会话或线程一个缓存)

    好吧,下面的马克说我使用的是DI/IOC。马克称之为反模式。

    Truewill同意Mark的评估,即我在Mark回复中使用了DI/IOC的评论: “大+ 1”。我也在想同样的事情——操作人员自己滚了一个DI/IOC容器。”

    但在他的评论中,特鲁威尔说我是 使用DI是为了 它是反模式的原因。

    这似乎意味着这是一个反模式,无论是否使用DI…

    我想发生了一些混乱,对此我应该道歉。首先,我的问题从使用单例开始。这只是一种反模式。这成功地将问题与我试图通过暗示一个错误的需求来实现的模式混淆了。单亲父母是不需要的,所以我为这个暗示道歉。

    请参阅我的“更新1”部分,了解详细信息。另请参阅“更新2”部分,讨论代表我试图描述的模式的flyweight模式。

    现在,飞锤模式可能是反模式。我不认为是这样,但可以讨论一下。但是,Mark和Truewill,我向您保证,我不会以任何方式使用DI/IOC,诚实的,也不会试图暗示DI/IOC是这种模式的要求。

    真的很抱歉弄混了。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Gabriel Ščerbák    14 年前

    它看起来像是GOF书中的经典飞锤设计图案。它不使用散列图覆盖单例工厂,但通常通过重用已创建的对象(许多其他对象引用该对象)来节省空间和性能。看看这个图案。

        2
  •  1
  •   C.Evenhuis    14 年前

    对象本身不遵循单例模式,因此将获取的对象称为单例可能会令人困惑。

    回收工厂怎么样?:]

        3
  •  1
  •   Mark Seemann    14 年前

    你描述的结构本质上是 DI容器 用作 静态服务定位器 (哪个) I consider an anti-pattern )

    这个服务定位器创建的每个服务都有一个所谓的 单体寿命 . 大多数DI容器在几个可用的生命周期中都支持这一点。