代码之家  ›  专栏  ›  技术社区  ›  Dai Bok

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

  •  9
  • Dai Bok  · 技术社区  · 16 年前

    这是对 what is so bad about singletons

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

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

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

    谢谢你的帮助!

    3 回复  |  直到 16 年前
        1
  •  8
  •   Lorenzo Boccaccia    16 年前

    免责声明:我在这里用Java语言

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

    考虑程序标准输出:该资源的访问需要由单个访问点来保护,以便允许写入的同步,这就是为什么您在Java中有例如Syt.OUT作为静态实例的原因。

    问题是,当你开始有独生子女的时候,你需要知道你正在做什么的每一个细节,因为你对你的独生子女阶级做了很多严格的假设,最重要的是它将是系统中唯一的一个阶级。然后您开始使用它,假设它始终是您的资源的一个入口点,然后出现了令人讨厌的错误,因为您的类现在已经部署在EJB服务器上,并且每个EJB上下文都有它自己的单例,对于从服务器重新加载的每个JSP再加一个单例,对于您的SI每次加一个单例。ngleton已被序列化和反序列化(因为您可能忘记重写readresolve()方法)。

    所以这就是为什么单例必须小心使用的原因,现在被认为是一个反模式,尽管它们对于预期的使用是完全有用的。

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

    因此,用singleton作为对资源进行共享访问的手段是不好的,因为您正在硬编码查找资源(并忽略singleton陷阱)的方法,同时让singleton为同步目的控制资源是完全可以接受的。

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

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

        2
  •  2
  •   lwho    16 年前

    在我看来,没有“非此即彼,非此即彼”。一个类可以同时实现几个设计模式。我会说,实现对外部数据库访问的类在任何情况下都是代理(在本例中是远程代理)。如果您认为缓存是一个额外的功能,那么它也是一个修饰器。

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

    如果有多个客户机访问类似的数据,那么如果他们共享同一个cachingdbproxy,显然会受益。一个客户机所需的数据可能已经被另一个客户机所需要,因此可以从缓存中检索数据,而不必执行昂贵的数据库访问。

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

    因此,应该有多少cachingdbproxy,取决于使用情况。cachingdbproxy不应在没有充分理由的情况下限制其使用,因此不应强制只存在一个实例,因此,在这种情况下,cachingdbproxy不应是singleton,imho。只有客户机才能知道有多少cachingDBproxy适合他们。

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

        3
  •  0
  •   Jeff Sternal    16 年前

    我只能想象代理模式用于在缓存数据和加载数据之间进行代理(又称懒惰加载)。

    某种程度上:

    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并完成。

    /* 免责声明:代码无效,只是出于演示目的的伪代码。 */