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

时间:2022-09-02 08:01:03

Here is my sample abstract singleton class:

以下是我的示例抽象单例类:

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

And here is the concrete implementation:

具体实施如下:

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

Unfortunately I can't get the static code in class B to execute, so the instance variable never gets set. I have tried this:

不幸的是,我无法在B类中获取静态代码来执行,所以实例变量没有设置。

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

and this

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

Running both these in the eclipse debugger the static code never gets executed. The only way I could find to get the static code executed is to change the accessibility on B's constructor to public, and to call it.

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

I'm using sun-java6-jre on Ubuntu 32bit to run these tests.

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

5 个解决方案

#1


19  

Abstract Singleton? Doesn't sound viable to me. The Singleton pattern requires a private constructor and this already makes subclassing impossible. You'll need to rethink your design. The Abstract Factory pattern may be more suitable for the particular purpose.

摘要单吗?对我来说听起来不可行。单例模式需要一个私有构造函数,这使得子类化成为不可能。你需要重新考虑你的设计。抽象工厂模式可能更适合于特定用途。

#2


8  

Well from your post, even if it's not clearly stated, sounds like you want the abstract class to play two different roles. The roles are :

从你的文章中,即使没有明确说明,听起来你想要抽象类扮演两个不同的角色。角色:

  • the abstract factory role for a (singleton) service that can have multiple substitutable implementations,
  • 一个(单例)服务的抽象工厂角色,它可以有多个可替换的实现,
  • the service interface role,
  • 服务接口的作用,

plus you want the service to be singleton enforce 'singletoness' in the entire family of classes, for some reason it's not enough for you to cache the service instance.

另外,您希望服务在整个类的家庭中强制执行“singletoness”,因为某些原因还不足以缓存服务实例。

This is fine.

这是很好。

Somebody will say it smells very bad because "violates separation of concerns" and "singletons and unit testing don't go well together".

有人会说这很难闻,因为“违反了关注点隔离”和“单件和单元测试不协调”。

Someone else will say it's ok-ish because you give put responsibility of instantiating the right children in the family itself and also expose more fluent interface overall since you don't need mediation of a factory that does nothing else than exposing a static method.

其他人会说,这是ok的,因为你给了一个负责实例化家庭中正确的孩子的责任,同时也暴露了更流畅的界面,因为你不需要一个工厂的中介,除了暴露静态方法外什么也不做。

What is wrong is that after you want the children to be responsible of selecting what implementation should the parent factory method return. It's wrong in terms of design because you are delegating to all children what can be simply pushed up and centralized into the abstract superclass and also it shows you are mixing together patterns that are used in different contexts, Abstract Factory (parent decide what family of classes clients are going to get) and Factory Method (children factories select what the clients will get).

错误的是,在您希望孩子们负责选择什么实现时,父工厂方法返回。的设计是错误的因为你是委托给所有的孩子都可以简单地推高,集中到抽象超类,也显示了混合在一起的模式在不同的上下文中,使用抽象工厂(父决定家庭类的客户会得到)和工厂方法(儿童工厂选择客户将会得到的东西)。

Factory Method is not just not required but also not possible with factory methods since it is centered on implementing or overriding "instance" methods. There is no such thing as override for a static methods, nor for a constructor.

工厂方法不仅不需要,而且也不可能使用工厂方法,因为它以实现或覆盖“实例”方法为中心。对于静态方法和构造函数来说,不存在重写这样的东西。

So going back to the initial good or bad idea of an abstract singleton that selects which behaviour to expose there are several ways to solve the initial problem, One could be the following, looks bad but i guess is near to what you were looking for:

所以回到最初的好或坏的概念,一个抽象的单例,它选择了哪些行为来揭示它有几种方法来解决最初的问题,一个可能是下面的,看起来很糟糕,但是我猜它接近于你要找的东西:

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

The parent could also use reflection.

父类也可以使用反射。

More test friendly and extension friendly solution is simply to have children that are not singleton but packaged into some internal package that you will document as "private" and abstract parent that can expose the "singleton mimiking" static getInstance() and will cache the children instances enforcing that clients always get the same service instance.

测试更加友好和扩展友好的解决方案是有孩子,不是单但打包成一些内部包,您将文档作为“私人”和抽象父能暴露出“单例mimiking”静态getInstance()和孩子们将缓存实例执行,客户总是得到相同的服务实例。

#3


4  

A.getInstance() will never call a derived instance since it's statically bound.

getinstance()不会调用派生实例,因为它是静态绑定的。

I would separate the creation of the object from the actual object itself and create an appropriate factory returning a particular class type. It's not clear how you'd parameterise that, given your example code - is it parameterised via some argument, or is the class selection static ?

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

You may want to rethink the singleton, btw. It's a common antipattern and makes testing (in particular) a pain, since classes under test will provide their own instance of that class as a singleton. You can't provide a dummy implementation nor (easily) create a new instance for each test.

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

#4


4  

Singletons are kind of yucky. Abstract insists on inheritance which you more often than not want to avoid if possible. Overall I'd rethink if what you are trying to do is the simplest possible way, and if so, then be sure to use a factory and not a singleton (singletons are notoriously hard to substitute in unit tests whereas factories can be told to substitute test instances easily).

单身是有点恶心。抽象强调继承,如果可能的话,你更经常要避免。总的来说,如果你想要做的是最简单的方法,如果是这样,那么一定要使用工厂,而不是单例(单例在单元测试中很难替代,而工厂可以轻松地替代测试实例)。

Once you start looking into implementing it as a factory the abstract thing will sort itself out (either it will clearly be necessary or it may factor out easily in place of an interface).

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

#5


2  

In addition to problems others have pointed out, having the instance field in A means that you can only have one singleton in the entire VM. If you also have:

除了其他人指出的问题之外,在A中拥有实例字段意味着在整个VM中只能有一个singleton。如果你也有:

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

... then whichever of B or C gets loaded last will win, and the other's singleton instance will be lost.

…然后,B或C中的任何一个将会被加载,而另一个实例将丢失。

This is just a bad way to do things.

这是一种糟糕的做事方式。

#1


19  

Abstract Singleton? Doesn't sound viable to me. The Singleton pattern requires a private constructor and this already makes subclassing impossible. You'll need to rethink your design. The Abstract Factory pattern may be more suitable for the particular purpose.

摘要单吗?对我来说听起来不可行。单例模式需要一个私有构造函数,这使得子类化成为不可能。你需要重新考虑你的设计。抽象工厂模式可能更适合于特定用途。

#2


8  

Well from your post, even if it's not clearly stated, sounds like you want the abstract class to play two different roles. The roles are :

从你的文章中,即使没有明确说明,听起来你想要抽象类扮演两个不同的角色。角色:

  • the abstract factory role for a (singleton) service that can have multiple substitutable implementations,
  • 一个(单例)服务的抽象工厂角色,它可以有多个可替换的实现,
  • the service interface role,
  • 服务接口的作用,

plus you want the service to be singleton enforce 'singletoness' in the entire family of classes, for some reason it's not enough for you to cache the service instance.

另外,您希望服务在整个类的家庭中强制执行“singletoness”,因为某些原因还不足以缓存服务实例。

This is fine.

这是很好。

Somebody will say it smells very bad because "violates separation of concerns" and "singletons and unit testing don't go well together".

有人会说这很难闻,因为“违反了关注点隔离”和“单件和单元测试不协调”。

Someone else will say it's ok-ish because you give put responsibility of instantiating the right children in the family itself and also expose more fluent interface overall since you don't need mediation of a factory that does nothing else than exposing a static method.

其他人会说,这是ok的,因为你给了一个负责实例化家庭中正确的孩子的责任,同时也暴露了更流畅的界面,因为你不需要一个工厂的中介,除了暴露静态方法外什么也不做。

What is wrong is that after you want the children to be responsible of selecting what implementation should the parent factory method return. It's wrong in terms of design because you are delegating to all children what can be simply pushed up and centralized into the abstract superclass and also it shows you are mixing together patterns that are used in different contexts, Abstract Factory (parent decide what family of classes clients are going to get) and Factory Method (children factories select what the clients will get).

错误的是,在您希望孩子们负责选择什么实现时,父工厂方法返回。的设计是错误的因为你是委托给所有的孩子都可以简单地推高,集中到抽象超类,也显示了混合在一起的模式在不同的上下文中,使用抽象工厂(父决定家庭类的客户会得到)和工厂方法(儿童工厂选择客户将会得到的东西)。

Factory Method is not just not required but also not possible with factory methods since it is centered on implementing or overriding "instance" methods. There is no such thing as override for a static methods, nor for a constructor.

工厂方法不仅不需要,而且也不可能使用工厂方法,因为它以实现或覆盖“实例”方法为中心。对于静态方法和构造函数来说,不存在重写这样的东西。

So going back to the initial good or bad idea of an abstract singleton that selects which behaviour to expose there are several ways to solve the initial problem, One could be the following, looks bad but i guess is near to what you were looking for:

所以回到最初的好或坏的概念,一个抽象的单例,它选择了哪些行为来揭示它有几种方法来解决最初的问题,一个可能是下面的,看起来很糟糕,但是我猜它接近于你要找的东西:

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

The parent could also use reflection.

父类也可以使用反射。

More test friendly and extension friendly solution is simply to have children that are not singleton but packaged into some internal package that you will document as "private" and abstract parent that can expose the "singleton mimiking" static getInstance() and will cache the children instances enforcing that clients always get the same service instance.

测试更加友好和扩展友好的解决方案是有孩子,不是单但打包成一些内部包,您将文档作为“私人”和抽象父能暴露出“单例mimiking”静态getInstance()和孩子们将缓存实例执行,客户总是得到相同的服务实例。

#3


4  

A.getInstance() will never call a derived instance since it's statically bound.

getinstance()不会调用派生实例,因为它是静态绑定的。

I would separate the creation of the object from the actual object itself and create an appropriate factory returning a particular class type. It's not clear how you'd parameterise that, given your example code - is it parameterised via some argument, or is the class selection static ?

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

You may want to rethink the singleton, btw. It's a common antipattern and makes testing (in particular) a pain, since classes under test will provide their own instance of that class as a singleton. You can't provide a dummy implementation nor (easily) create a new instance for each test.

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

#4


4  

Singletons are kind of yucky. Abstract insists on inheritance which you more often than not want to avoid if possible. Overall I'd rethink if what you are trying to do is the simplest possible way, and if so, then be sure to use a factory and not a singleton (singletons are notoriously hard to substitute in unit tests whereas factories can be told to substitute test instances easily).

单身是有点恶心。抽象强调继承,如果可能的话,你更经常要避免。总的来说,如果你想要做的是最简单的方法,如果是这样,那么一定要使用工厂,而不是单例(单例在单元测试中很难替代,而工厂可以轻松地替代测试实例)。

Once you start looking into implementing it as a factory the abstract thing will sort itself out (either it will clearly be necessary or it may factor out easily in place of an interface).

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

#5


2  

In addition to problems others have pointed out, having the instance field in A means that you can only have one singleton in the entire VM. If you also have:

除了其他人指出的问题之外,在A中拥有实例字段意味着在整个VM中只能有一个singleton。如果你也有:

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

... then whichever of B or C gets loaded last will win, and the other's singleton instance will be lost.

…然后,B或C中的任何一个将会被加载,而另一个实例将丢失。

This is just a bad way to do things.

这是一种糟糕的做事方式。