如何使用代理模式来替换单例?

时间:2010-01-19 15:06:40

标签: design-patterns singleton

这是对what is so bad about singletons

中的一些评论的回应

有人建议可以使用代理模式代替单例来缓存DB数据。但我看不出优势,事实上单身人士似乎更“可控”。

让我详细说明这个问题。假设你有一个数据库,有大量的数据,从不改变,所以它可以被认为是只读的,为什么代理模式比单例更好地建模这个数据缓存呢?

(PS:如果你要说“因为它更''可测试'!” - 请详细说明,我仍然习惯这些概念)

感谢您的帮助!

3 个答案:

答案 0 :(得分:8)

免责声明:我在这里用java术语说话

单身现在被认为是反模式,因为它们最近被滥用了很多,因为它们是一种快速方便的方式在应用程序之间共享数据 - 这有点过分了设计模式,更适合提供访问控制共享资源。

考虑一个程序标准输出:该资源的访问需要通过单点访问来保护以允许写入的同步,这就是为什么你将例如System.out作为java中的静态实例的原因。

问题是,当你开始单身时,你需要知道你正在做的每一个细节,因为你在你的单身人士课上做了很多严格的假设,这是最重要的一个。系统中唯一的一个类。然后你开始使用它,假设它总是一个单一的资源入口点,然后出现讨厌的bug,因为你的类现在已部署在一个ejb服务器上,每个ejb上下文都有它自己的单例,还有一个单独的从服务器重新加载的每个jsp,以及每次单例序列化和反序列化时的一个单例(因为你可能忘记覆盖readResolve()方法)。

所以这就是为什么单身必须经常使用的原因,现在它被认为是反模式,尽管它们对它们的预期用途完全有用。

在数据库缓存的情况下,使用此“缓存”资源的代理来让每个类需要缓存是更好的方法,因此您可以添加逻辑以“查找资源”代理本身,而不是将逻辑绑定到缓存单例的检索,这可能会或可能不会取决于环境。

因此,使用单例作为对资源进行共享访问的手段很少,因为您正在硬编码查找资源的方法(并忽略单例陷阱),而单例控制资源以实现同步目的是完全可以接受。

想到信号量,只有当你总能获得相同的信号量时才能使用信号量。在后一种情况下,可能存在的问题是从您需要访问该信号量的任何地方访问单例:在这里,您需要一些类来包装单例并提供对信号量本身生命周期的更精细控制。

代理旨在涵盖“在整个系统中提供资源”的角色,无论是单个应用程序,客户端服务器系统,同一系统的不同组件等,还有èrpxy方法的附加好处检索资源是不透明的。你可以让他们为你提供一个包含缓存值的hashmap的单例,你可以让他们访问网络中的memcached somwhere,你可以让他们在测试期间读取csv,所有这些都不会改变你从应用程序中调用它们的方式。< / p>

答案 1 :(得分:2)

在我看来,没有“或者,或者”。类可以同时实现多种设计模式。我会说实现对外部数据库的访问的类在任何情况下都是代理(在这种情况下是一个远程代理)。如果你认为缓存是一个额外的功能,那么它也是一个装饰器。

所以,真正的问题是,它是否也应该是一个单身人士?让我们假设只有一个外部数据库。是否只需要一个CachingDBProxy?我想说,这取决于用途:

如果有多个客户端访问类似的数据,如果他们共享相同的CachingDBProxy,他们可以明显受益。另一个客户端可能已经需要一个客户端所需的数据,因此可以从缓存中检索它,而不必执行对数据库的昂贵访问。

另一方面,一些客户可能会访问非常不同的数据。因此,如果我们假设CachingDBProxy仅缓存一组客户端的有限数量的数据访问,则可能会丢弃另一组客户端仍然需要的数据,从而导致缓存性能下降。在这种情况下,即使只有一个数据库,也可以使用多个CachingDBProxies(当然,这可以假设并发访问)。

那么,应该有多少CachingDBProxies,取决于使用情况。 CachingDBProxy不应该没有充分的理由限制它的使用,所以它不应该强制只有一个实例,因此,在这种情况下,CachingDBProxies应该不是Singleton,恕我直言。只有客户才能知道有多少CachingDBProxies对他们有利。

另一方面,如果我们有一个特定资源的代理,一次只能处理一个访问,它可能必须是一个单例。但这与上述情况截然不同。这里的要求直接来自Proxy负责的区域(其目的是引导对该特定资源的访问)。

答案 2 :(得分:0)

我只能想象代理模式用于缓存数据之间的代理并加载数据(也称为延迟加载)。

排序:

class DbProxy
{
  private static Data cache = null; // Sort-of Singleton

  public static Data GetData(String query)
  {
    if (DbProxy.cache == null)
    {
      // Data = Do Stuff to read Data
      DbProxy.cache = Data;
    }
    return DbProxy.cache;
  }
}

这样做的好处是使用它的代码不必关心Data已经存在,它只需要调用GetData并完成。

/ * 免责声明:代码无效,仅为演示目的而伪 * /