java单例继承_关于继承:如何在Java中实现抽象单例类?

本文讨论了在Java中尝试实现一个抽象单例类所遇到的问题,包括静态初始化块不执行导致的实例化问题,以及对单例模式和抽象工厂模式的比较。作者指出,这样的设计可能违反关注点分离原则,不利于单元测试,并提出使用工厂模式作为替代方案。
摘要由CSDN通过智能技术生成

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

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 32位上使用sun-java6-jre运行这些测试。

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

Singleton中的私有构造函数的目的是防止其他任何人实例化它。我已经在这里实现了这一点-您无法实例化一个抽象类,并且该子类具有私有构造函数。

它不会强制子类仅是抽象的,并且仅具有私有构造函数。

Singleton不需要私有构造函数(请参阅我的答案以使用公共构造函数解决此问题)。

抱歉,您的答案是无用的。私有构造函数是单例模式的关键要求。如果您没有一个人,那么您实际上没有一个人。阅读的食物:butunclebob.com/ArticleS.UncleBob.SingletonVsJustCreateOne

您试图让抽象类扮演两个非常不同的角色:

的抽象工厂角色

(单例)服务可以具有

多重可替代

实现,

服务

界面角色,

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

有人(我会)说它闻起来很不好,原因有很多

它违反了关注点分离

单例使得不可能进行单元测试",

等等

别人会说这没关系,它不需要很多不同的基础结构,并且具有某种流利的接口,您可以在一些非常常见的第??三方(传统)Java API中看到这种接口。

不好的部分是要求孩子选择父工厂方法应返回的实现。

该责任应该被推高并集中到抽象超类中。否则,您将在非常不同的上下文中使用的模式混合在一起,分别是Abstract Factory(父级决定客户端将要获得的类家族)和Factory Method(子工厂选择客户端将要获得的类)。

实际上,工厂方法也不可行,因为您不能覆盖静态方法,也不能构造函数。

尽管有一些(丑陋的)方法可以实现您的目标:

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

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

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

"如果(...)返回B.getInstance();返回C.getInstance();"违反"打开关闭委托人"

单身人士有点讨厌。抽象坚持继承,如果可能的话,您经常会希望避免继承。总的来说,我会重新考虑是否要尝试的是最简单的方法,如果是这样,那么一定要使用工厂而不是单例(众所周知,在单元测试中很难替代单个子,而可以告诉工厂替代单个子。测试实例)。

一旦您开始考虑将其实现为工厂,抽象的东西就会自行整理(显然是有必要的,或者可以很容易地将其分解为接口)。

+1指出单例是反模式。

单身人士有自己的位置,但是无论您做什么,通常都有更好的设计模式。日志可能是我能想到的一个最好的例子-这样,您不必传递变量就可以从任何地方访问它。

使用Injection可以更好地完成日志。单例日志无法轻松告诉您该行来自哪个类,而Log l = new Log(this.getClass())或其中的一些变量可以。通过注入,它只是@inject日志或类似的东西。即使是记录日志,也很难测试一个单例。同样,单身人士是最差的选择(但可以使用)

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

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

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

A.getInstance()不需要调用派生实例,因为它返回了实例变量。派生实例(类B)设置实例变量。除非静态代码块不运行。如果我无法解决此问题,则工厂模式可能是我唯一的选择。

除了指出的问题外,在A中具有instance字段意味着整个VM中只能有一个单例。如果您还有:

public class C extends A {

private C() { }

static {

instance = new C();

}

//...implementations of my abstract methods...

}

...然后最后装载的B或C中的任何一个将获胜,而另一个的单例实例将丢失。

这只是做事的一种坏方法。

谢谢-和其他说相同话的人。这凸显出我的真正问题是我应该如何选择接口的几种实现中的哪一种。工厂或立面看起来是不错的选择。

我发现了在抽象类中使用Singleton的更好方法,该类使用静态Map维护子类的实例。

public abstract class AbstractSingleton {

private static Map registryMap = new HashMap();

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值