单一责任原则和组织DI接口的正确方法

时间:2016-10-29 11:27:09

标签: java oop inheritance dependency-injection interface

假设您有存储在磁盘上的文件,您需要加载它们。文件有3种类型,其中包含*.ext1*.ext2*.ext3。所以我有三个load方法,每个方法应该通过不同的锁同步。我应该用DI框架将这种行为注入我的服务中。 以下哪种方式更好?

似乎第一种方式是显而易见的。但是什么让我不确定 - 单一责任的原则以及你永远不会想要加载所有三个扩展的事实:你只会加载1& 3或仅2& 3.

1)所有文件类型加载器的通用接口:

public interface FileLoader {
    void loadFilesOfExt1();
    void loadFilesOfExt2();
    void loadFilesOfExt3();
}

//The most obvoius solution, but doesn't it break the principle of single responsibility?
//It also has one more dwawback - I want to synchronize each method by different lock, and it requires using of locks
public class FileLoaderImpl implements FileLoader {
    public void loadFilesOfExt1() { /* impl */ }
    public void loadFilesOfExt2() { /* impl */ }
    public void loadFilesOfExt3() { /* impl */ 
}

2)每种文件的特定界面:

//It looks pretty weird to have three interfaces with the same API
public interface Ext1Loader { void load(); }
public interface Ext2Loader { void load(); }
public interface Ext3Loader { void load(); }
public class Ext1LoaderImpl extends Ext1Loader { public synchronized void load() { /* impl */ } }
public class Ext2LoaderImpl extends Ext2Loader { public synchronized void load() { /* impl */ } }
public class Ext3LoaderImpl extends Ext3Loader { public synchronized void load() { /* impl */ } }

3)界面层次结构:

//Isn't it an overengeneering? 3 Empty interfaces
public interface FileLoader { void load(); }
public interface Ext1Loader extends FileLoader {}
public interface Ext2Loader extends FileLoader {}
public interface Ext3Loader extends FileLoader {}
public class Ext1LoaderImpl extends Ext1Loader { public synchronized void load() { /* impl */ } }
public class Ext2LoaderImpl extends Ext2Loader { public synchronized void load() { /* impl */ } }
public class Ext3LoaderImpl extends Ext3Loader { public synchronized void load() { /* impl */ } }

4)单一界面:

//This makes me feel like all implementations `Ext1Loader`, `Ext2Loader` and `Ext3Loader` are interchangable. 
//But actually you may want to inject `Ext1Loader` nad `Ext3Loader` simultaneously into your class
public interface FileLoader { public void load(); }
public class Ext1Loader implements FileLoader { public synchronized void load() { /* impl */ } }
public class Ext2Loader implements FileLoader { public synchronized void load() { /* impl */ } }
public class Ext3Loader implements FileLoader { public synchronized void load() { /* impl */ } }

2 个答案:

答案 0 :(得分:2)

第三种解决方案在这里看起来像是最安全的解决方案。但是添加标记接口似乎不会过度工程。

我建议你考虑我对你的任务的看法:

do
{
    ...
}
while(!Display.isCloseRequested())

只有一个接口,实现将加载特定格式的文件。每个加载器都有一个唯一的名称,用于描述加载器将使用的文件类型。

public interface FileLoader {
    void load();
}

在那之后,我们需要一个按需生产装载机的工厂。

@Component("Ext1")
public class Ext1FileLoader implements FileLoader {
    public synchronized void load() {}
}

以下解决方案可能更简洁,但我们无法启用惰性初始化模式。看看:

@Component
public class FileLoaderFactory implements ApplicationContextAware {

    // to get new loaders
    private ConfigurableApplicationContext context;

    // to keep already pulled out instances
    private Map<String, FileLoader> loaders;

    public FileLoader getFileLoaderByFileType(String type) {
        // trying to use a lazy-loading mode

        return loaders.get(type) == null ? getFromContext(type) : loaders.get(type);
    }

    private FileLoader getFromContext(String type) {
        // getting a bean from the context, putting it into a map and returning a value

        FileLoader loader = context.getBean(type, FileLoader.class);
        loaders.put(type, loader);
        return loader;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        // a casting here is to get broader functionality from a context instance

        this.context = (ConfigurableApplicationContext) context;
    }

}

答案 1 :(得分:0)

  1. 定义工厂类以提供通用加载函数,如

    public Class FileLoader {

    //定义类似

    的地图

    私人地图extensionToLoaderMap;

    public static File load(StringfilePath){

    //提取扩展并从Map

    获取适当的加载器

    //在这里做一些有意义的事。

    }

  2. 定义一个界面,例如

    public interface ExtensionLoader {

    public File loadExtension(String filePath);

    }

  3. 现在您可以定义尽可能多的扩展加载器。

  4. 您可以使用您的DI框架来存储所有这些映射,例如here对于spring框架的描述

  5. 这将使您能够添加/修改扩展加载器的功能,而无需修改实际文件加载器的Java代码。