单个类

时间:2017-12-11 15:38:42

标签: java multithreading design-patterns singleton

我正在设计一个可以支持不同数据源的模块。 我的模块将用户的公司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类中同步大量代码,这可能是个问题。 有没有办法可以避免同步并使其线程安全而不会影响性能?

3 个答案:

答案 0 :(得分:1)

  

我似乎无法避免在hashmap中使用相同的实例

您可以使用开关而不是一堆ifs。

切换枚举(Java 5)

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)

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的实现。所有这些都假设它也是线程安全的。