2014-12-04 108 views
0

我遇到了一个奇怪的问题。我有一个接口,它的实现往往是无状态的。所以,我希望他们成为单身人士。如何从类名字符串中获取单例实例作为字符串

我得到的实现类名字符串。例如

String clazz = "com.foo.Bar"; 

我有一个规则工厂来获得IRule实现的实例。

public class RulesFactory { 

    private static final Logger logger = LoggerFactory.getLogger(RulesFactory.class); 

    @SuppressWarnings("unchecked") 
    public static <T extends IRule> T getRuleInstance(String clazz) { 
     try { 
      Class<?> ruleObject = Class.forName(clazz); 
      Method factoryMethod = ruleObject.getMethod("getInstance"); 
      return (T) factoryMethod.invoke(null); 
     } catch (ClassNotFoundException e) { 
      logger.error("ClassNotFoundException", e); 
     } catch (IllegalAccessException e) { 
      logger.error("IllegalAccessException", e); 
     } catch (SecurityException e) { 
      logger.error("SecurityException", e); 
     } catch (NoSuchMethodException e) { 
      logger.error("NoSuchMethodException", e); 
     } catch (IllegalArgumentException e) { 
      logger.error("IllegalArgumentException", e); 
     } catch (InvocationTargetException e) { 
      logger.error("InvocationTargetException", e); 
     } 
     return null; 
    } 
} 

上面代码抛出NullPointerException如果该类不具有静态getInstance()方法。在Java 6中,我不能在接口中使用静态方法。我不想创建IRule实现的多个实例。如果我可以强制执行一个静态方法并调用该静态方法,我将获得兑现实例。但我无法做到这一点。如何解决这个问题呢?

+0

另一个想法:有保护构造函数包的实现。将它们放在与RulesFactory相同的包中,然后基于clazz使用“new”创建实现,并按类名称将其存储在映射中。在创建新实例前始终检查地图。 – Blitzkr1eg 2014-12-04 07:44:22

+0

如果**方法是静态的,则Null是传递调用**的有效值。所以检查你的'getInstance()'方法。 – Kayaman 2014-12-04 07:45:14

+0

@Kayaman是的。这是有效的。但是,如何强制getInstance被客户端实现,也是静态方法? – phoenix 2014-12-04 07:46:41

回答

1

有不同的利弊几种解决方案:

  1. 不要使用static方法。如果该方法不是静态的,则可以将其添加到IRule,并因此强制该方法存在。
  2. 检查的factoryMethod预选赛,并抛出一个描述异常的时候都没有static

对于解决方案#1,你需要一个Map<String,IRule>。当调用getRuleInstance()时,请检查地图中的实例。如果没有,则使用界面中的方法创建一个并将其放入地图。这样,你可以使实例变成单例。

与此同时,您可以获取实例的所有字段,并确保所有字段都是final以强制执行无状态。

如果您的应用程序是多线程的,请确保您使用并发映射并正确同步。

示例代码:

private Map<String, IRule> rules = Maps.newHashMap(); 

public static <T extends IRule> T getRuleInstance(String clazz) { 
    try { 
     synchronized(rules) { 
      IRule result = rules.get(clazz); 
      if(null == result) { 
       result = clazz.newInstance(); 
       rules.put(clazz, result); 
      } 
      @SuppressWarnings("unchecked") 
      T tmp = (T) result; 
      return tmp; 
     } 
    } catch (Exception e) { 
     log("Unable to create IRule for {}", clazz); 
    } 
} 
+0

我认为,它已成为选择之一的“鸡与鸡蛋”问题。除非我有一个实例,我不能调用invoke方法,然后调用getInstance()没有意义。任何想法如何解决这个问题? – phoenix 2014-12-04 11:15:12

+0

其实我试图用Bill Pugh模式实例化实现。所以,它创造了两个实例创建的麻烦。一个用于调用invoke,另一个用于调用getInstance()。 – phoenix 2014-12-04 11:35:47

+0

代表#1,调用'ruleObject.newInstance()'。实际上,你应该将'ruleObject'重命名为'ruleType'。 – 2014-12-04 12:02:58

1

你是让你的生活不必要艰苦。

如果您还记得伟大的“enum单身模式”,并要求所有实现者使用它,

public enum Foo implements IRule 
{ 
    INSTANCE; 

    // IRule implementation methods here 

    public String toString() { return "the sole Foo instance"; } 
} 

整个RulesFactory变得简单:

private static final ConcurrentMap<String, IRule> instancesMap 
              = new ConcurrentHashMap<String, IRule>(); 

public static IRule getRuleInstance(String clazz) { 
    try { 
     IRule iRuleInstance=instancesMap.get(clazz); 
     if(iRuleInstance!=null) return iRuleInstance; 
     Class<? extends IRule> ruleObject=Class.forName(clazz).asSubclass(IRule.class); 
     IRule[] enumConstants=ruleObject.getEnumConstants(); 
     if(enumConstants==null || enumConstants.length!=1) { 
     logger.error("InvalidClassException", 
        new InvalidClassException(clazz, "not a singleton enum")); 
     return null; 
     } 
     iRuleInstance=enumConstants[0]; 
     instancesMap.put(clazz, iRuleInstance); 
     return iRuleInstance; 
    } catch (ClassNotFoundException e) { 
     logger.error("ClassNotFoundException", e); 
    } 
    return null; 
} 

有关“enum Singleton模式”的伟大的事情是,它已经保证了singleton属性,因此上面的代码不作任何尝试检测并发查找,因为它们保证返回相同的单个实例。