如何在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来运行这些测试。


答案 1

抽象单例?对我来说听起来不可行。单例模式需要一个构造函数,这已经使得子类化变得不可能。你需要重新思考你的设计。抽象工厂模式可能更适合特定目的。private


答案 2

你试图让一个抽象类扮演两个截然不同的角色:

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

最重要的是,您还希望服务是单例的,并对整个类系列强制实施“单一性”,出于某种原因,您不考虑缓存服务实例。

有人(我会)会说它闻起来很糟糕,由于多种原因,它违反了关注点的分离,单例使单元测试变得不可能“,等等。

其他人会说它还可以,它不需要很多不同的基础设施,并且具有您在一些非常常见的第三方(遗留)Java API中看到的流利的界面。

不好的部分是要求子级选择父工厂方法应该返回的实现。这种责任应该被推高,并集中到抽象的超阶级中。否则,您将混合在一起在非常不同的上下文中使用的模式,抽象工厂(父级决定客户端将获得的类系列)和工厂方法(子工厂选择客户端将获得的内容)。

工厂方法实际上也是不可能的,因为您不能重写静态方法,也不能重写构造函数。

不过,有一些(丑陋的)方法可以实现您的目标:

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

父级还可以使用反射、缓存实例等。

一个对测试和扩展更友好的解决方案是简单的标准关注点分离。子级本身不再是单例,但是您将它们打包到一些内部包中,您将将其记录为“私有”,并且外部包中的公共抽象父级将处理子实例的缓存或池化,并在这些类上强制执行所需的任何实例化策略。


推荐