如何在Java中实现抽象的单例类?

这是我的示例抽象单例类:

public abstract class A { protected static A instance; public static A getInstance() { return instance; } //...rest of my abstract methods... } 

以下是具体实施:

 public class B extends A { private B() { } static { instance = new B(); } //...implementations of my abstract methods... } 

不幸的是我无法在B类中获取静态代码来执行,因此实例变量永远不会被设置。 我试过这个:

 Class c = B.class; A.getInstance() - returns null; 

和这个

 ClassLoader.getSystemClassLoader().loadClass("B"); A.getInstance() - return null; 

在eclipse调试器中运行这两个,静态代码永远不会被执行。 我可以找到执行静态代码的唯一方法是将B的构造函数的可访问性更改为public,并调用它。

我在Ubuntu 32bit上使用sun-java6-jre来运行这些测试。

摘要单身人士? 对我来说听起来不太可行。 Singleton模式需要private构造函数,这已经使子类化成为不可能。 你需要重新考虑你的设计。 抽象工厂模式可能更适合于特定目的。

从您的post开始,即使没有明确说明,听起来您希望抽象类扮演两个不同的角色。 角色是:

  • (单例)服务的抽象工厂角色,可以有多个可替换的实现,
  • 服务接口角色,

另外,您希望服务在整个类系列中单独执行’singletoness’,由于某种原因,您不足以缓存服务实例。

这可以。

有人会说它闻起来非常糟糕,因为“违反了关注点的分离”,“单身人士和单位测试并不能很好地结合在一起”。

其他人会说这是好的,因为你赋予了在家庭中实例化正确孩子的责任,并且整体上也暴露了更流畅的界面,因为你不需要调解工厂,除了暴露静态方法之外什么也不做。

有什么不对的是,您希望孩子们负责选择父工厂方法返回的实现。 这在设计方面是错误的,因为你委托给所有孩子,可以简单地将它们推到并集中到抽象超类中,并且它还表明你正在混合使用在不同上下文中使用的模式,抽象工厂(父级决定什么类的类)客户将获得)和工厂方法(儿童工厂选择客户将获得的)。

工厂方法不仅不是必需的,而且也不可能与工厂方法一起使用,因为它以实现或覆盖“实例”方法为中心。 对于静态方法,也没有构造函数的覆盖。

因此,回到一个抽象单例的最初好或坏的想法,选择要暴露哪些行为有几种方法来解决最初的问题,一个可能是以下,看起来很糟糕,但我想接近你正在寻找的:

 public abstract class A{ public static A getInstance(){ if (...) return B.getInstance(); return C.getInstance(); } public abstract void doSomething(); public abstract void doSomethingElse(); } public class B extends A{ private static B instance=new B(); private B(){ } public static B getInstance(){ return instance; } public void doSomething(){ ... } ... } //do similarly for class C 

父母也可以使用反思。

更多测试友好和扩展友好的解决方案只是让孩子不是单身,而是打包到一些内部包,你将记录为“私人”和抽象的父,可以暴露“单身模仿”静态getInstance(),并将缓存孩子强制客户端始终获得相同服务实例的实例。

A.getInstance()永远不会调用派生实例,因为它是静态绑定的。

我将对象的创建与实际对象本身分开,并创建一个返回特定类类型的适当工厂 。 鉴于您的示例代码 – 是通过某个参数进行参数化,还是类选择是静态的,您不清楚如何参数化?

你可能想重新考虑单身,顺便说一句。 这是一个常见的反模式 ,并使测试(特别是)成为一种痛苦,因为测试中的类将提供他们自己的该类实例作为单例。 您不能提供虚拟实现,也不能(轻松地)为每个测试创建新实例。

单身人士有点讨厌 。 摘要坚持inheritance,如果可能的话,你经常要避免 。 总的来说,我会重新思考,如果你想要做的是最简单的方法 ,如果是这样,那么一定要使用工厂而不是单身人士(单身人士在unit testing中很难替代,而工厂可以被告知替代轻松测试实例)。

一旦你开始考虑将它作为一个工厂实现,抽象的东西就会自行解决(要么它显然是必要的,要么可以很容易地代替接口)。

除了其他人指出的问题之外,在A中使用instance字段意味着在整个VM中只能有一个单例。 如果你还有:

 public class C extends A { private C() { } static { instance = new C(); } //...implementations of my abstract methods... } 

…然后最后加载的BC任何一个都将获胜,而另一个的单例实例将丢失。

这只是做事的坏方法。