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

发布于 2024-08-26 00:09:56 字数 781 浏览 6 评论 0 原文

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

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 构造函数的可访问性更改为公共,然后调用它。

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

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:

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

吻风 2024-09-02 00:09:56

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

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.

小傻瓜 2024-09-02 00:09:56

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

  • 抽象工厂角色
    (单例)服务可以有
    多重可替代
    实施、
  • 服务
    接口角色,

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

有人(我会)会说它闻起来很难闻,原因有多种
它违反了关注点分离,
单例使单元测试变得不可能”,
其他人会说这还

可以,它不需要很多不同的基础设施,并且具有您在一些非常常见的第​​三方(遗留)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

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

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

You are trying to get an abstract class play two very different roles:

  • the abstract factory role for a
    (singleton) service that can have
    multiple substitutable
    implementations,
  • the service
    interface role,

and on top of that you also want the service to be singleton and enforce 'singletoness' on the entire family of classes, for some reason you aren't considering caching the service instances.

Somebody (I would) will say it smells very bad, for multiple reasons
it violates separation of concerns,
singletons make unit testing impossible",
etc.

Somebody else will say it's ok-ish, it doesn't need a lot of different infrastructure and has kind of fluent-ish interface that you see in some very common third party (legacy) Java API.

The bad part is demanding the children to select what implementation should the parent factory method return.
That responsibility should be pushed up and centralised into the abstract superclass. Otherwise you are mixing together patterns that are used in very 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 also not practically possible because you can't override static methods, nor constructors.

There are some (ugly) ways to achieve your objective though:

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, cache instances, etc.

A more test and extension friendly solution is simply standard separation of concerns. The children aren't going to be singleton anymore per se, but you package them into some internal package that you will document as "private" and a public abstract parent in an external package will handle caching or pooling of children instances, enforcing whatever instantiation policy is required on these classes.

疯到世界奔溃 2024-09-02 00:09:56

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

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

顺便说一句,您可能想重新考虑单例。这是一种常见的反模式,并且使测试(尤其是测试)变得很痛苦,因为被测试的类将提供自己的该类实例作为单例。您无法提供虚拟实现,也无法(轻松)为每个测试创建新实例。

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

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.

凡间太子 2024-09-02 00:09:56

除了其他人指出的问题之外,在 A 中包含 instance 字段意味着整个虚拟机中只能有一个单例。如果您还有:

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

... 那么最后加载的 BC 中的一个将获胜,而另一个的单例实例将丢失。

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

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:

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.

This is just a bad way to do things.

另类 2024-09-02 00:09:56

单例有点令人讨厌。 Abstract 坚持继承,如果可能的话,您通常不希望避免。总的来说,我会重新考虑您想要做的是否是 最简单的方法,并且如果因此,请确保使用工厂而不是单例(单例在 单元测试,而工厂可以被告知轻松替换测试实例)。

一旦您开始考虑将其实现为工厂,抽象事物就会自行解决(要么它显然是必要的,要么它可以轻松地代替接口)。

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).

丢了幸福的猪 2024-09-02 00:09:56

我找到了一种在抽象类中使用 Singleton 的更好方法,它使用静态 Map 来维护子类的实例。

public abstract class AbstractSingleton {

    private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();

    AbstractSingleton() throws SingletonException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)) {
            throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized (registryMap) {
                if (registryMap.containsKey(clazzName)) {
                    throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    T instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (!registryMap.containsKey(clazzName)) {
            Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    AbstractSingleton instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    static class SingletonException extends Exception {
        private static final long serialVersionUID = -8633183690442262445L;

        private SingletonException(String message) {
            super(message);
        }
    }
}

来自:https://www.cnblogs.com/wang9192/p/3975748.html< /a>

I found a better way to use Singleton in abstract class, which use a static Map to maintain the instance of subclass.

public abstract class AbstractSingleton {

    private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();

    AbstractSingleton() throws SingletonException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)) {
            throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized (registryMap) {
                if (registryMap.containsKey(clazzName)) {
                    throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    T instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (!registryMap.containsKey(clazzName)) {
            Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    AbstractSingleton instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    static class SingletonException extends Exception {
        private static final long serialVersionUID = -8633183690442262445L;

        private SingletonException(String message) {
            super(message);
        }
    }
}

From: https://www.cnblogs.com/wang9192/p/3975748.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文