了解匕首2中的范围 [英] Understanding scopes in Dagger 2

查看:137
本文介绍了了解匕首2中的范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Dagger 2中有与范围有关的错误,我正在努力了解我如何解决它。



我有一个公司活动显示公司。当用户选择一个项目时,所选公司的员工将显示在 EmployeesActivity 中。当用户选择员工时,她的详细信息显示在 EmployeeDetailActivity 中。

  class Company {
列表<员工>雇员;
}

Class CompaniesViewModel contains公司和选定的一个(或 null ):

  class CompaniesViewModel {
列表<公司>企业;公司选择;
}

CompaniesActivity 参考 CompaniesViewModel

  class CompaniesActivity extends Activity {

@Inject
CompaniesViewModel viewModel;

@Override
protected void onCreate(Bundle b){
//更多东西
getComponent()。
showCompanies(viewModel.companies);
}

//更多东西

private onCompanySelected(公司公司){
viewModel.selected = company;
startActivity(new Intent(this,EmployeesActivity.class));
}

}

EmployeesViewModel 包含员工和选定的员工(或 null ):

  class EmployeesViewModel {
列表< Employee>雇员;
员工选择;
}

EmployeesActivity 引用 EmployeesViewModel

  class EmployeesActivity extends Activity {

@Inject
EmployeesViewModel viewModel;

@Override
protected void onCreate(Bundle b){
//更多东西
getComponent()。
showEmployees(viewModel.employees);
}

//更多东西

private onEmployeeSelected(Employee emp){
viewModel.selected = emp;
startActivity(new Intent(this,EmployeeDetailActivity.class));
}

}

最后,在$ code> EmployeeDetailActivity ,我从视图模型中选择了Employee并显示了其详细信息:

  class EmployeeDetailActivity extends活动{

@Inject
EmployeesViewModel viewModel;

@Override
protected void onCreate(Bundle b){
//更多东西
getComponent()。
showEmployeeDetail(viewModel.selected); // NullPointerException
}
}

我得到 NullPointerException 因为 EmployeesViewModel 中的实例EmployeesActivity EmployeeDetailActivity ,第二个 viewModel.selected null 。 p>

这是我的匕首模块:

  @Module 
class MainModule {

@Provides
@Singleton
public CompaniesViewModel offersCompaniesViewModel(){
CompaniesViewModel cvm = new CompaniesViewModel();
cvm.companies = getCompanies();
return cvm;
}

@Provides
public EmployeesViewModel provideEmployeesViewModel(CompaniesViewModel cvm){
EmployeesViewModel evm = new EmployeesViewModel();
evm.employees = cvm.selected.employees;
return evm;
}

}

请注意, CompaniesViewModel 是单例( @Singleton ),但 EmployeesViewModel 不是,因为它必须每次用户选择一个公司时,都会重新创建一个公司(员工列表中将包含其他项目)。



我可以设置每个用户选择一个公司,而不是创建一个新的实例,雇员到 EmployeesViewModel 。但我想要 CompaniesViewModel 是不可变的。



我该如何解决?任何建议将不胜感激。

解决方案

不幸的是,我认为您在这种情况下滥用DI框架,以及遇到的问题是代码气味 - 这些问题暗示你做错了事情。



为了将关键依赖关系(协作者对象)注入到顶级组件中,应使用DI框架,执行这些注入的逻辑应完全独立于业务逻辑的应用程序。



从一开始,一切看起来都不错 - 你使用Dagger注入 CompaniesViewModel EmployeesViewModel into Activity 。这可能是很好的(虽然我不会这样做),如果这些是真实的对象。但是,在您的情况下,这些是数据结构(因此您希望它们是不可变的)。



对象和数据结构之间的这种区别不是微不足道的,最好在Bob叔叔的清洁代码一书中描述。 本博客总结现在,如果您尝试使用DI框架注入数据结构,则最终将该框架转换为应用程序的数据提供者,从而委托部分业务功能进入它。例如:它看起来像 EmployeesViewModel 独立于 CompaniesViewModel ,但它是一个谎言 - 代码在 @Provides 方法将它们逻辑地绑定在一起,从而隐藏依赖关系。在这种情况下,良好的经验法则是,如果DI代码取决于注入对象的实现细节(例如调用方法,访问字段等),则通常表示关注问题分离不足。



两个具体的建议:


  1. 不要将业务逻辑与DI逻辑混合。在你的情况下 - 不要注入数据结构,而是注入对数据进行访问的对象(不好),或者在抽象数据的时候暴露所需的功能(更好)。

  2. I认为您尝试在多个屏幕之间共享视图模型不是一个非常强大的设计。对于每个屏幕,最好有一个View-Model的单独实例。如果您需要在屏幕之间共享状态,那么根据具体要求,您可以使用1)意图附加功能2)全局对象3)共享prefs 4)SQLite


I have an scope-related error in Dagger 2 and I'm trying to understand how I can solve it.

I have a CompaniesActivity that shows companies. When the user selects an item, selected company's employees are shown in EmployeesActivity. When the user selects an employee, her detail is shown in EmployeeDetailActivity.

class Company {
    List<Employee> employees;
}

Class CompaniesViewModel contains the companies and the selected one (or null):

class CompaniesViewModel {
    List<Company> companies;
    Company selected;
}

CompaniesActivity has a reference to CompaniesViewModel:

class CompaniesActivity extends Activity {

    @Inject
    CompaniesViewModel viewModel;

    @Override
    protected void onCreate(Bundle b) {
        //more stuff
        getComponent().inject(this);
        showCompanies(viewModel.companies);
    }

    //more stuff

    private onCompanySelected(Company company) {
        viewModel.selected = company;
        startActivity(new Intent(this, EmployeesActivity.class));
    }

}

Class EmployeesViewModel contains the employees and the selected one (or null):

class EmployeesViewModel {
    List<Employee> employees;
    Employee selected;
}

EmployeesActivity has a reference to EmployeesViewModel:

  class EmployeesActivity extends Activity {

        @Inject
        EmployeesViewModel viewModel;

        @Override
        protected void onCreate(Bundle b) {
            //more stuff
            getComponent().inject(this);
            showEmployees(viewModel.employees);
        }

        //more stuff

        private onEmployeeSelected(Employee emp) {
            viewModel.selected = emp;
            startActivity(new Intent(this, EmployeeDetailActivity.class));
        }

    }

Finally, in EmployeeDetailActivity, I get selected Employee from view model and show her detail:

  class EmployeeDetailActivity extends Activity {

        @Inject
        EmployeesViewModel viewModel;

        @Override
        protected void onCreate(Bundle b) {
            //more stuff
            getComponent().inject(this);
            showEmployeeDetail(viewModel.selected); // NullPointerException
        }
    }

I get NullPointerException because EmployeesViewModel instance in EmployeesActivity is not the same as the EmployeeDetailActivity and, in the second one, viewModel.selected is null.

This is my dagger module:

@Module
class MainModule {

    @Provides
    @Singleton
    public CompaniesViewModel providesCompaniesViewModel() {
        CompaniesViewModel cvm = new CompaniesViewModel();
        cvm.companies = getCompanies();
        return cvm;
    }

    @Provides
    public EmployeesViewModel providesEmployeesViewModel(CompaniesViewModel cvm) {
        EmployeesViewModel evm = new EmployeesViewModel();    
        evm.employees = cvm.selected.employees;
        return evm;
    }

}

Note that CompaniesViewModel is singleton (@Singleton) but EmployeesViewModel is not, because it has to be recreated each time user selects a company (employees list will contain other items).

I could set the company's employees to EmployeesViewModel each time user selects a company, instead of create a new instance. But I would like CompaniesViewModel to be immutable.

How can I solve this? Any advise will be appreciated.

解决方案

Unfortunately, I think that you abuse DI framework in this case, and the issues that you encounter are "code smells" - these issues hint that you're doing something wrong.

DI frameworks should be used in order to inject critical dependencies (collaborator objects) into top level components, and the logic that performs these injections should be totally independent of the business logic of your application.

From the first sight everything looks fine - you use Dagger in order to inject CompaniesViewModel and EmployeesViewModel into Activity. This could have been fine (though I wouldn't do it this way) if these were real "Objects". However, in your case, these are "Data Structures" (therefore you want them to be immutable).

This distinction between Objects and Data Structures is not trivial, and is best described in Uncle Bob's "Clean Code" book. This blog post summarizes it pretty well.

Now, if you try to inject Data Structures using DI framework, you ultimately turn the framework into "data provider" of the application, thus delegating part of the business functionality into it. For example: it looks like EmployeesViewModel is independent of CompaniesViewModel, but it is a "lie" - the code in @Provides method ties them together logically, thus "hiding" the dependency. Good "rule of thumb" in this context is that if DI code depends on implementation details of the injected objects (e.g. calls methods, accesses fields, etc.) - it is usually an indication of insufficient separation of concerns.

Two specific recommendations:

  1. Don't mix business logic with DI logic. In your case - don't inject data structures, but inject objects that either provide access to the data (bad), or expose the required functionality while abstracting the data (better).
  2. I think that your attempt of sharing a View-Model between multiple screens is not a very robust design. It would be better to have a separate instance of View-Model for each screen. If you need to "share" state between screens, then, depending on the specific requirements, you could do this with 1) Intent extras 2) Global object 3) Shared prefs 4) SQLite

这篇关于了解匕首2中的范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆