当依赖注入不起作用时,避免使用单例

时间:2014-03-18 09:59:32

标签: java dependency-injection singleton android-sqlite android-contentprovider

我有两个内容提供商,我们称之为数据和元数据。数据在写入操作期间调用meta来编写一些元数据。但是,在引擎盖下,两者都在同一个数据库上工作,并且为了避免在事务期间出现死锁,它们也必须位于同一个数据库 connection 上。

目前,我通过对我的SQLiteOpenHelper子类使用单例设计模式来实现这一点,但这使得很难模拟测试(因为它,我的测试写入与app相同的数据库)本身=坏)。

通常我会通过依赖注入来解决这个问题但是框架依赖于无参数构造函数的存在来实例化提供者,所以我不能这样做。

理论上,我可以将我的SQLiteOpenHelper类更改为单例映射,从Context的(弱引用)映射到实例,但这看起来更糟。

解决此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:0)

如果你需要一个no-args构造函数,你可以使用setter来添加所需的依赖项。

Singleton holder的一个例子是

interface Service {
}

enum ServceHolder { /* no instance */;
    static volatile Service service;
    public static Service getService() { return service; }
    public static void setService(Service s) { service = s; }
}

答案 1 :(得分:0)

我现在以不同的方式解决了这个问题。 我的解决方案涉及将访问SQLiteOpenHelper实例存储在一个包私有字段中,然后只通过一个懒惰的初始化getter访问它,该getter将与其" buddy"现有实例的内容提供程序。

protected SQLiteOpenHelper getHelper() {
    if (dbHelper == null) {
        ContentProviderClient client = getContext()
                .getContentResolver()
                .acquireContentProviderClient(MetaContentProvider.AUTHORITY);
        try {
            MetaContentProvider buddy = (MetaContentProvider) client.getLocalContentProvider();

            if (buddy.dbHelper != null) {
                dbHelper = buddy.dbHelper;
            }
        } catch (ClassCastException x /* Wasn't a MetaContentProvider */) {
        } catch (NullPointerException x /* MetaContentProvider is unavailable (it's client was null) */) {
        } finally {
            client.release();
        } 
    }
    if (dbHelper == null) {
        dbHelper = new DbHelper(getContext());
    }
    return dbHelper;
}

这是一个很好的解决方案吗?绝不是。这是一个相当丑陋的讽刺,产生一个隐藏的依赖,就像单身模式一样,它只在第一个地方起作用,因为两个提供者都在同一个包中(虽然这并不罕见)。

然而,它确实完全摆脱了单身模式,同时确保每个上下文只有一个共享SQLiteOpenHelper - 这是它的优势所在单身模式。完全不需要修改测试代码来引导提供者远离生产数据库 - 因为测试位于不同的上下文中,它自动需要新的共享帮助程序。此外,它不需要仅为测试目的而暴露或引入API(我强烈反对)。