2014-12-04 3 views
0

У меня возникает странная проблема. У меня есть интерфейс, реализация которого имеет тенденцию быть апатридом. Поэтому я хочу, чтобы они были одиночными.Как получить экземпляр singleton из имени класса в виде строки

Я получаю имена классов реализации как строки. Например

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 создайте «новую» реализацию и сохраните ее на карте по имени класса. Всегда проверяйте карту перед созданием нового экземпляра. – Blitzkr1eg

+0

Null - допустимое значение, которое нужно передать, чтобы вызвать **, если ** метод является статическим. Поэтому проверьте методы 'getInstance()'. – Kayaman

+0

@ Кайаман Да. Это действительно. Но как обеспечить, чтобы getInstance выполнялся клиентами, а также как статический метод? – phoenix

ответ

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

+0

На самом деле я пытаюсь создать экземпляр реализации, используя шаблон Билла Пью. Таким образом, создается проблема создания двух экземпляров. Один для вызова invoke, а другой - вызовом getInstance(). – phoenix

+0

для # 1, вызовите 'ruleObject.newInstance()'. На самом деле, вы должны переименовать 'ruleObject' в' ruleType'. –

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 одноплодной шаблон» является то, что уже гарантирует одноплодное свойство, таким образом, приведенный выше код не делает любой попытайтесь обнаружить параллельные запросы, поскольку они гарантированно возвратят один и тот же экземпляр.

Смежные вопросы