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

Design patterns 如何使用代理模式替换单例?,design-patterns,singleton,proxy-pattern,Design Patterns,Singleton,Proxy Pattern,这是对中的一些评论的回应 有人建议可以使用代理模式而不是单例来缓存DB数据。但我看不出它的优势,事实上,单身似乎更“可控” 让我详细说明这个问题。假设您有一个拥有大量数据的数据库,它从不更改,因此可以将其视为只读,那么为什么代理模式比单例模式更适合建模此数据缓存 (附言:如果你想说“因为它更‘可测试’!”——请详细说明,我仍在习惯这些概念) 谢谢你的帮助 我只能想象代理模式用于在缓存数据和加载数据之间进行代理(也称为延迟加载) 有点: class DbProxy { private stat

这是对中的一些评论的回应

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

让我详细说明这个问题。假设您有一个拥有大量数据的数据库,它从不更改,因此可以将其视为只读,那么为什么代理模式比单例模式更适合建模此数据缓存

(附言:如果你想说“因为它更‘可测试’!”——请详细说明,我仍在习惯这些概念)


谢谢你的帮助

我只能想象代理模式用于在缓存数据和加载数据之间进行代理(也称为延迟加载)

有点:

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;
  }
}
这样做的好处是,使用它的代码不必关心数据是否已经存在,只需调用GetData并完成即可

/* 免责声明:代码无效,只是出于演示目的的伪代码
*/在我看来,没有“非此即彼,或”。一个类可以同时实现多个设计模式。我想说,实现对外部数据库访问的类在任何情况下都是代理(在本例中是远程代理)。如果你考虑缓存一个额外的功能,它也是一个装饰器。 所以,真正的问题是,它是否也应该是单身?假设只有一个外部数据库。是否只需要一个CachingDBProxy?我想说,这取决于用途:

如果有多个客户端访问类似的数据,那么如果它们共享相同的CachingDBProxy,显然可以从中获益。一个客户机所需的数据可能已经被另一个客户机所需,因此可以从缓存中检索数据,而不必对数据库执行代价高昂的访问

另一方面,一些客户机可能访问非常不同的数据块。因此,如果我们假设CachingDBProxy只缓存一组客户机访问的有限数据,那么可能会抛出另一组客户机仍然需要的数据,从而导致缓存性能下降。在这种情况下,即使只有一个数据库,拥有多个CachingDBProxies也是合理的(当然,这假设并发访问是可能的)

因此,应该有多少CachingDBProxies,取决于用途。CachingDBProxy不应该在没有充分理由的情况下限制其使用,因此它不应该强制执行只有一个实例,因此,在这种情况下,CachingDBProxy不应该是单例,IMHO。只有客户才能知道有多少恶作剧对他们有益


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

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

singleton现在被认为是一种反模式,主要是因为它最近被滥用了很多,因为它是跨应用程序共享数据的一种快速方便的方式——这在某种程度上是设计模式的过度扩展,更适合于提供对共享资源的访问控制

考虑一个程序标准输出:该资源的访问需要由一个访问点来保护,以允许写操作的同步,这就是为什么您将System.out作为java中的静态实例

问题是,当你开始使用singleton时,你需要知道你正在做的每一个细节,因为你对你的singleton类做了很多严格的假设,最重要的是它将是系统中唯一的一个类。然后开始使用它,假设它始终是资源的单个入口点,然后出现了严重的错误,因为您的类现在已经部署在ejb服务器上,每个ejb上下文都有自己的单例,再加上从服务器重新加载的每个jsp都有一个单例,每次序列化和反序列化一个单例(因为您可能忘记重写readResolve()方法)

因此,这就是为什么必须非常小心地使用singleton,并且现在被认为是一种反模式,尽管它们对于预期用途完全有用

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

因此,简单地说,使用单例作为共享资源访问的手段是不好的,因为您正在硬编码查找资源的方法(并忽略单例陷阱),同时让单例控制资源以实现同步是完全可以接受的

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

代理旨在涵盖“在整个系统中提供资源”的角色,无论是单个应用程序、客户机-服务器系统、同一系统的不同组件等等,还有一个额外的好处,即使用èrpxy检索资源的方法是不透明的。您可以让他们向您提供一个包含缓存值哈希映射的单例,您可以让他们访问memcached som,在网络上,您可以让他们在测试期间读取csv,而无需更改方式