我正在设计一个可以支持不同数据源的模块。 我的模块将用户的公司ID作为输入,我必须根据公司ID调用相应的类。 我试图加入一些好的设计并尽可能避免条件陈述。
我有一个使用此方法的FetchDataSource单例类。
public class FetchDataSourceSingleton {
private static Map<String, Communicator> communicatorMap;
public static Communicator getCommunicatorInstance(String dataSourceType) {
if (communicatorMap == null || communicatorMap.isEmpty())
populateCommunicatorMap();
if (communicatorMap.containsKey(dataSourceType))
return communicatorMap.get(dataSourceType);
return null;
}
.... other methods including populateCommunicatorMap()
}
&#34;操&#34;是一个接口,通信器映射将返回适当的实例。 这是同一单例类中的populateCommunicatorMap()方法。
private static void populateCommunicatorMap() {
communicatorMap = new HashMap<String, Communicator>();
communicatorMap.put("AD", new ADCommunicator());
communicatorMap.put("DB2", new DB2Communicator());
communicatorMap.put("MYSQL", new MYSQLCommunicator());
}
ADCommunicator,DB2Communicator和MYSQLCommunicator将实现Communicator接口。
代码似乎适用于我的测试草稿。 我唯一关心的是HashMap将为同一类型的所有通信请求返回相同的对象。如果我想避免使用条件语句,我似乎无法避免在hashmap中使用相同的实例。否则不是hashmap,我可以像这样进行调用。
Communicator comm;
if (type = "AD") comm = new ADCommunicator();
if (type = "DB2") comm = new DB2Communicator();
if (type = "MYSQL") comm = new MYSQLCommunicator();
我通过使用hashmap返回基于类型的实例来避免这种情况。 但是,在我获得相同实例的情况下,我无法避免单身人士问题。
在多线程环境中,一次需要支持数十万个通信请求,考虑到我需要在每个Communicator类中同步大量代码,这可能是个问题。 有没有办法可以避免同步并使其线程安全而不会影响性能?
答案 0 :(得分:1)
我似乎无法避免在hashmap中使用相同的实例
您可以使用开关而不是一堆ifs。
将type
更改为Java 5+中的枚举,然后您可以启用它。我推荐一般用于类型安全的枚举。
// type is-a enum Communicator.TYPE
switch(type) {
case AD: return new ADCommunicator();
case DB2: return new DB2Communicator();
case MYSQL: return new MYSQLCommunicator();
default: return null;
}
Java 8可以直接切换字符串。
// type is-a String
switch(type) {
case "AD": return new ADCommunicator();
case "DB2": return new DB2Communicator();
case "MYSQL": return new MYSQLCommunicator();
default: return null;
}
切换枚举的速度与地图一样快,如果不是更快的话。打开字符串将与Map一样快。
或者有工厂地图:
private final static Map<String, Factory<? extends Communicator>> map;
static {
map.put("AD", ADCommunicatorFactory.getInstance());
//...
map.put(null, NullFactory<Communicator>.getInstance());
} // populated on class-load. Eliminates race from lazy init
// on get
return map.get(type).make();
或者使用反射API来创建实例,但是最好只使用条件。
// on init
Map<String, Class<? extends Communicator>> map = new HashMap<>();
map.put("AD", ADCommunicator.class);
// on get
try {
return (Communicator) map.get(type).newInstance();
} catch(InstantiationException | IllegalAccessException | NullPointerException e) {
return null;
}
P.S。 这听起来像是过早的优化。我怀疑确定使用哪个Communicator会成为系统中的瓶颈。
答案 1 :(得分:1)
如果所有的通信器都可以使用空参数列表构造函数构造,那么您可以将通信器的类型(类)存储在映射中而不是实例中。然后,您可以从communicatorMap中查找类型(java.lang.Class),并使用java.lang.Class.newInstance()实例化一个新实例。
例如:
public interface Communicator {
void communicate();
}
public class Communicator1 implements Communicator {
public void communicate() {
System.out.println("communicator1 communicates");
}
}
public class Communicator2 implements Communicator {
public void communicate() {
System.out.println("communicator2 communicates");
}
}
public class CommuniicatorTest {
public static void main(String[] args) throws Exception {
Map<String, Class<? extends Communicator>> communicators = new HashMap<String, Class<? extends Communicator>>();
communicators.put("Comm1", Communicator1.class);
communicators.put("Comm2", Communicator2.class);
Communicator comm2 = communicators.get("Comm2").newInstance();
comm2.communicate();
System.out.println("comm2: " + comm2);
Communicator anotherComm2 = communicators.get("Comm2").newInstance();
anotherComm2.communicate();
System.out.println("anotherComm2: " + anotherComm2);
}
}
结果:
communicator2 communicates
comm2: pack.Communicator2@6bc7c054
communicator2 communicates
anotherComm2: pack.Communicator2@232204a1
答案 2 :(得分:0)
Assylias对于使用静态初始化程序是正确的。它在您的类加载时运行,这可以保证在类发生任何其他事件之前加载映射。
您没有显示地图的声明;我认为它是静态的。
private final static Map<String, Communicator> communicatorMap;
static {
communicatorMap = new HashMap<>();
communicatorMap.put("AD", new ADCommunicator());
communicatorMap.put("DB2", new DB2Communicator());
communicatorMap.put("MYSQL", new MYSQLCommunicator());
}; // populated on class-load. Eliminates race from lazy init
剩下的问题是Communicator的实现。所有这些都假设它也是线程安全的。