我们应该封装单例吗?我们是否应该首先尝试从单例继承?

5
一个Singleton类是否允许拥有子类?我们应该对它进行封印吗?这样做的利弊如何?
如果想从一个Singleton类继承,我们需要将构造函数从private改为protected。在C#中这没有问题,但是在Java中,protected关键字会使得子类和同一包内的类都可以访问构造函数。这意味着不仅继承我们的Singleton的类可以访问构造函数,同一包内的其他类也可以。
我对所有这些事情有点困惑。也许我正在为了一些无关紧要的事情而大惊小怪?到目前为止,我从来没有必要去尝试从一个Singleton类继承,所以也许这只是一个学术问题!
谢谢
5个回答

8
更好的解决方案是使用IoC容器框架处理类的“单例”(生命周期)方面。此时,您可以使用POJO并简单地继承它。
编辑:一些可能有用的链接:
- .NET依赖注入容器列表(IOC) - 开源反转控制容器(Java) - .NET中的依赖注入(书籍) - Mark Seemann的.NET博客-依赖注入类别 寻找处理生命周期管理的容器。您希望能够配置容器(充当通用工厂),以便对“Singleton”类的实例请求始终返回相同的实例(至少对于相同的容器)。通常您只在应用程序的最高级别上拥有一个容器实例。

IoC 也被描述为依赖注入(DI)。 - Stephen C
1
  1. 在处理对象的生命周期(即存在多少实例)时,应将其与对象内部的实际逻辑/代码分开处理。混合两者会导致代码不美观。
- matt b

8
是的,单例应该是密封的。不,它们不应该被继承。原因是单例的主要(真正的)行为是在某个预定时间创建其自身的实例。由于这种功能是静态的,无法被覆盖,所以它必须被复制。一旦您开始复制,就会有多个单例实例,这并没有太多意义。否则,您将遇到竞争条件或其他冲突,因为“派生”的单例会竞争控制全局实例,这不仅没有意义,而且很危险。虽然有一些hackish解决单例继承问题的方法,但那只是hack。单例模式并不适合继承。

但是,如果构造函数是私有的,如何制作更多的实例?只要无法调用构造函数,实例就无法创建。再次强调,我不明白为什么Singleton类应标记为sealed? - Abdulkarim Kanaan
我故意尝试继承非密封Singleton类,但失败了。编译器会给出错误信息:“'Singleton.Singleton()'不可访问,因为它的保护级别”。Singleton是我的类名。 - Abdulkarim Kanaan

4

“现实生活中的编程通常需要恰好这种方法” [需要引用] - Aaronaught
许多人强烈反对从单例继承,我强烈反对首先使用单例(或其他全局数据)。 - TrueWill
@TrueWill,我理解你的观点,但软件项目的现实情况迫使我们采取技术方法。例如,5年前我曾参与开发一家大型银行的企业贷款Web应用程序。它有清晰分离的层,处理数百个报告层,每个层执行特定的XSLT转换。然而,同时在线用户数量增长得如此之多,以至于我们在生产服务器上物理上耗尽了内存。通过利用全局静态变量来存储只需读取一次且对所有会话保持一致的信息,我们成功将内存使用量减少到原来的1/50。 - code4life
当然,缓存或池化有限资源的需求是现实存在的。全局静态变量和单例并不是唯一实现这一目标的方式。使用依赖注入,特别是在使用DI/IoC容器框架时,也可以实现相同的目标。这可以提高可测试性和可维护性。 - TrueWill

3

如果你可以从单例扩展,那么就意味着你可以拥有多个实例。这与单例的整体思想相矛盾。只需将其制作成“普通”类,并编写相应的代码,使其仅实例化一次并永久使用相同实例即可。如果需要的话,依赖注入可以大有帮助。


这并不意味着它可以有多个实例。然而,必须有一种机制来确保子类可以将它们的实例设置为“唯一”的实例。 - zneak
1
这只有在构造函数是私有的且实例是静态的情况下才可能实现。总之,这根本就没有任何意义。实例化一个简单类只需一次,然后在应用程序的生命周期内永久使用它,这有多难呢? - BalusC
我认为我们只是彼此误解了。我的意思是,从单例类继承并不意味着它可以有多个实例。 - zneak

2

如果你想在单例模式中使用继承,应该将可继承的状态和行为放入一个抽象基类中,并将单例定义为(最终的)子类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接