简体   繁体   English

使用 Dagger 2 注入子类的最佳方法是什么?

[英]What is the best way to inject subclasses using Dagger 2?

I recently started using Dagger 2 to manage the depency injections of my app.我最近开始使用 Dagger 2 来管理我的应用程序的依赖注入。 In order to make some classes testable, I started creating many classes that I need to inject.为了使某些类可测试,我开始创建许多需要注入的类。 I was able to do so, however, the process to get these new classes injected look a little bit complicaded to me.我能够这样做,但是,注入这些新类的过程对我来说看起来有点复杂。 Lets take an example:让我们举个例子:

I would like to test, for instance, the method onCreateViewHolder() from my RecyclerViewAdapter.例如,我想测试我的 RecyclerViewAdapter 中的onCreateViewHolder()方法。 To do so, I've created a Factory that returns the ViewHolder based on the given LayoutType:为此,我创建了一个 Factory,它根据给定的 LayoutType 返回 ViewHolder:

public class ViewHolderFactor {

    public RecyclerView.ViewHolder getViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = this.getLayoutInflater(parent.getContext());
        View view;
        switch (LayoutType.fromInteger(viewType)) {
            case SMALL_VERTICAL:
                view = inflater.inflate(R.layout.rsc_util_item_small, parent, false);
                return new ViewHolder.ItemViewHolder(view);
            case LARGE_VERTICAL:
                view = inflater.inflate(R.layout.rsc_util_item_large, parent, false);
                return new ViewHolder.ItemViewHolder(view);
        }
        return null;
    }

    private LayoutInflater getLayoutInflater(Context context) {
        return LayoutInflater.from(context);
    }

}

By moving the above code to a separated class, I'm able to perform my unit test using:通过将上面的代码移动到一个单独的类,我可以使用以下方法执行我的单元测试:

@RunWith(JUnit4.class)
public class TestViewHolderFactor extends TestCase {

    ViewHolderFactor viewHolderFactor;

    @Test
    public void testGetViewHolder () {
        this.viewHolderFactor = Mockito.mock(ViewHolderFactor.class);
        ViewGroup viewGroup = Mockito.mock(ViewGroup.class);
        Mockito.when(viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger())).thenCallRealMethod();
        Context context = Mockito.mock(Context.class);
        Mockito.when(viewGroup.getContext()).thenReturn(context);
        LayoutInflater layoutInflater = Mockito.mock(LayoutInflater.class);
        Mockito.when(viewHolderFactor.getLayoutInflater(context)).thenReturn(layoutInflater);
        Mockito.when(layoutInflater.inflate(R.layout.rsc_util_item_small, viewGroup, false)).thenReturn(Mockito.mock(View.class));
        RecyclerView.ViewHolder result = viewHolderFactor.getViewHolder(viewGroup, LayoutType.SMALL_VERTICAL.toInteger());
        assertNotNull(result);
    }

}

Problem is: now, to make the app work, I will also have to inject the outter class that holds an instance to the factor (something that I wasn't doing before creating ViewHolderFactor).问题是:现在,为了使应用程序正常工作,我还必须将保存实例的外部类注入到因子(在创建 ViewHolderFactor 之前我没有做的事情)。 At end, I will have a Dagger configuration like that:最后,我将有一个这样的 Dagger 配置:

@Module
public class ModuleBusiness {
    @Provides
    public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
        return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
    }
}

Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter that contains the ViewHolderFactory.其中 CharacterUIService 是创建包含 ViewHolderFactory 的 RecyclerViewAdapter 新实例的类。

public class 
    private ViewHolderFactor 
    @Inject
    public CharacterUIService(ViewHolderFactor viewHolderFactor) {
        this.mViewHolderFactor = viewHolderFactor;
    }

    // ...
}

I need a member from ViewHolderFactor in order to inject it.我需要 ViewHolderFactor 的成员才能注入它。

My worries regards the need to create new global variables and inscrease the number of parameters being passed in the constructor.我担心需要创建新的全局变量并增加构造函数中传递的参数数量。 Is there a better way to inject these subclasses or can I consider it as a good practice in order to allow Unit Tests?是否有更好的方法来注入这些子类,或者我可以将其视为允许单元测试的好方法吗?

can I consider it as a good practice in order to allow Unit Tests?我可以将其视为允许单元测试的好习惯吗?

First of all—in my personal opinion—I think that you are overdoing the testing.首先——在我个人看来——我认为你做的测试过度了。 In the test case that you provided you are basically testing the android framework itself .在您提供的测试用例中,您基本上是在测试android 框架本身 If you want to test the factory, you should test whether a small or large item is returned, rather than the view being not null.如果要测试工厂,则应测试返回的是小项还是大项,而不是视图不为空。

But yes, yours seems like a reasonable approach to allow for unit testing.但是,是的,您的方法似乎是允许单元测试的合理方法。

Where CharacterUIService is the class that creates a new instance of the RecyclerViewAdapter其中 CharacterUIService 是创建 RecyclerViewAdapter 新实例的类

You might want to overthink the role of CharacterUIService .您可能想过度考虑CharacterUIService的作用。 "that creates a new instance" sounds like something that should rather be handled by dagger, since you are already using it. “创建一个新实例”听起来像是应该由 dagger 处理的东西,因为您已经在使用它了。

will also have to inject the outter class that holds an instance to the factory还必须将持有实例的外部类注入工厂

Why?为什么? ViewHolderFactor (sic!) has no dependencies itself. ViewHolderFactor (原文如此!)本身没有依赖项。 And even if it did, what is stopping you from just creating and injecting it with dagger?即使它做到了,是什么阻止你仅仅用匕首创建和注入它?

class ViewHolderFactor {
    @Inject
    ViewHolderFactor() { // allow for constructor injection
    }
}

class YourAdapter {
    ViewHolderFactor mFactor;

    @Inject // allow for constructor injection
    YourAdapter (ViewHolderFactor factor) {/**/}
}
// now dagger knows how to create that adapter

And just create let dagger create that adapter?只是创建让匕首创建该适配器?


And generally, if you support constructor injection, then you can remove your @Provides providesSomething() methods.通常,如果您支持构造函数注入,那么您可以删除您的@Provides providesSomething()方法。 Just remove the whole method.只需删除整个方法。 You have an @Inject annotation on the constructor, so make use of that and let dagger write that code for you.您在构造函数上有一个@Inject注释,因此请使用它并让 dagger 为您编写该代码。

// CharacterUIService can be constructor injected.
@Provides // DAGGER WILL DO THIS FOR YOU
public CharacterUIService provideCharacterService(Picasso picasso, NotificationUtil notificationUtil, ViewHolderFactor viewHolderFactor) {
    // REMOVE ALL OF THIS.
    return new CharacterUIService(picasso, notificationUtil, viewHolderFactor);
}

So while I think you do a good job by making things testable, you should have again a good look on dagger, since you are obviously mixing many things up.所以虽然我认为你通过使事情可测试做得很好,但你应该再次好好看看 dagger,因为你显然把很多东西混在一起了。 Have a good look on constructor injection and make use of it, it saves you a whole lot of work.好好看看构造函数注入并利用它,它可以为您节省大量工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM