2017-06-04 64 views
1

OrderDBManager,UserDBManager,ProductDBManager扩展DBManager。 我正在尝试创建一个单例DBManager Factory 以获取不同DBManager类型类的实例。Singletons工厂使用泛型和反射

但我有一个问题,使用泛型返回确切DBManager类型类用户想要的实例。现在它只适用于DBManager本身。 我将不胜感激您的帮助!

public class DBManagerFactory { 

    private static final Map<String, DBManager> instances = new HashMap<>(); 

    private static final String DB_MANAGER_PACKAGE_NAME = "ua.Test.db."; 

    public static DBManager getInstance(Class<? extends DBManager> dbManagerClass) { 
      String dbManagerClassName = dbManagerClass.getSimpleName(); 
      DBManager dbManager = instances.get(dbManagerClassName); 

      if(dbManager == null){ 
       Class clazz = Class.forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName); 
       dbManager = (DBManager)clazz.newInstance(); 
       instances.put(dbManagerClassName, dbManager);  
      } 
      return dbManager;            
    }}                 
+0

这是什么'类的点。 forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName)'?调用者已经为你提供了一个'Class'对象。 – Holger

回答

0

如果你需要得到明确的返回类型,那么你可以使用generic factory method,如下:

class Base {} 

class A extends Base {} 

class B extends Base {} 

class InitializationFailedException extends RuntimeException { 
    public InitializationFailedException(Throwable cause) { 
     super(cause); 
    } 
} 

class Factory { 

    private static Logger log = LoggerFactory.getLogger(Factory.class); 

    private static final ConcurrentHashMap<Class<? extends Base>, Base> instances = new ConcurrentHashMap<>(); 

    public static <T extends Base> T getInstance(Class<T> tClass) { 
     if (tClass == null) { 
      throw new NullPointerException(); 
     } 
     return (T) instances.computeIfAbsent(tClass, (tClass1) -> { 
      try { 
       return tClass1.newInstance(); 
      } catch (InstantiationException | IllegalAccessException e) { 
       log.error("Unable to create singleton value for " + tClass1.getCanonicalName(), e); 
       throw new InitializationFailedException(e); 
      } 
     }); 
    } 

} 

用这种方法你可以得到你的子类型引用创建:

A aInstance = Factory.getInstance(A.class); 
B bInstance = Factory.getInstance(B.class); 

为您的问题方法签名可能看起来像:

public static <T extends DBManager> T getInstance(Class<T> dbManagerClass); 

情况下,地图应该看起来像:

private static final Map<Class<? extends DBManager>, DBManager> instances = new HashMap<>(); 
+0

好的。那么地图申报应该怎么看? –

+0

我已经用可能的选项更新了答案 – udalmik

+0

获得显式返回类型的逻辑是可以的,只需要注意HashMap,它不同步。在生产环境中,应修改此代码以避免破坏hashMap。 –

1

但我使用泛型返回的确切 DBManager型类用户想要的实例有问题。

要返回方法中的特定类,应该指定范围方法参数化类型,该类型允许类的客户端指定方法应该返回的类型。

你可以这样声明方法:

public static <T extends DBManager> T getInstance(Class<T> dbManagerClass) { 

为了避免不安全的类型转换,你可以通过使用Class#cast(Object)方法为您执行从Map<String, DBManager>检索到的DBManager实例转换为T extends DBManager类型铸造DBManager实例之前,类型检查:

T dbManager = dbManagerClass.cast(instances.get(dbManagerClassName)); 

最后,顺便检索类:

Class clazz = Class.forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName); 

不是必需的,因为您已经将类作为方法的参数。
只是直接使用它。

这里是实现这种解决方案的示例代码:

public class DBManagerFactory { 

    private static final Map<String, DBManager> instances = new HashMap<>(); 

    public static <T extends DBManager> T getInstance(Class<T> dbManagerClass) { 

     final String dbManagerClassName = dbManagerClass.getSimpleName(); 

     T dbManager = dbManagerClass.cast(instances.get(dbManagerClassName)); 

     if (dbManager == null) { 
     try { 
      dbManager = dbManagerClass.newInstance(); 
     } catch (InstantiationException | IllegalAccessException e) { 
      e.printStackTrace(); // or better log it 
      throw new RuntimeException("faillure during reflection instantiation for class " + dbManagerClass.getName(), e); 
     } 
     instances.put(dbManagerClassName, dbManager); 
     } 

     return dbManager; 
    } 

} 

有了这些改进,客户端类可以这样调用方法:

DBManager instance = DBManagerFactory.getInstance(DBManager.class); 
DBManagerChild child = DBManagerFactory.getInstance(DBManagerChild.class); 
+1

你不应该使用像'(T)...'这样的未经检查的转换。相反,使用'dbManagerClass.cast(...)'。作为附注,如果OP使用'dbManagerClass.newInstance()',则返回的引用已经是'T'类型。 OP的整个名称处理看起来都是错误的... – Holger

+0

@Holger你是完全正确的。我会避免改变OP用来检索类的方式:'class clazz = Class.forName(DB_MANAGER_PACKAGE_NAME + dbManagerClassName);'正如你注意到的那样,它没有被正确地使用。我只是修改了一个更好更全面的答案。 – davidxxx