Home > Mobile >  How to use an abstract factory with realizations of an interface that have different constructor par
How to use an abstract factory with realizations of an interface that have different constructor par

Time:12-08

Everything works fine till the constructors of the realizations are the same. However, I have a sort of dilemma when the constructors are different. Is it okay or maybe there is an architecture issue? Here is the code:

public class CategoryViewFactory : ICategoryViewFactory
{
    private readonly ActiveProgressions _activeProgressions;

    public CategoryViewFactory(ActiveProgressions activeProgressions)
    {
        _activeProgressions = activeProgressions;
    }

    public ICategoryView? Create(CategoryType type, Category category)
    {
        return type switch
        {
            CategoryType.Category => new CategoryView(category),
            CategoryType.Subcategory => new SubcategoryView(category, _activeProgressions),
            _ => null
        };
    }
}

ActiveProgression is a singleton service which I inject using the container. Is it okay? And what I should do if, for instance, ActiveProgression is a transient service? How can I create SubcategoryView in this case?

The idea of putting extra parameters in Create() seems quite bad, but it also seems the only solution in this case.

CodePudding user response:

CategoryViewFactory's Create method acts as a Composer. A Composer is part of your application's Composition Root that is responsible for composing instances. In this case the Create method is using Pure DI rather can moving that responsibility to a DI Container.

As long as CategoryViewFactory is defined in the application's startup path, a.k.a. the Composition Root, everything what you're doing is perfectly fine. It's even fine for ActiveProgression to call back into the DI Container, *as long as ActiveProgression is part of the Composition Root as well. When ActiveProgression is defined outside the context of the Composition Root, it's use of the DI Container can be considered a Service Locator, which is a well-known anti-pattern.

And what I should do if, for instance, ActiveProgression is a transient service?

As long as CategoryViewFactory is transient as well, there will be no problem. When CategoryViewFactory is singleton, however, ActiveProgression will become a Captive Dependency.

Although, what you're doing is perfectly fine, it might be less obvious for CategoryViewFactory to use Pure DI, while other parts of your Composition root use a DI Container. You can consider CategoryViewFactory to forward the responsibility of composition to the DI Container. For instance:

public class CategoryViewFactory : ICategoryViewFactory
{
    private readonly MyDiContainer container;

    public CategoryViewFactory(MyDiContainer container)
    {
        this.container = container;
    }

    public ICategoryView? Create(CategoryType type, Category category)
    {
        var view = this.CreateInternal(type);
        view?.Initialize(category);
        return view;
    }

    private ICategoryView? CreateInternal(CategoryType type)
    {
        return type switch
        {
            CategoryType.Category => container.GetService<CategoryView>(),
            CategoryType.Subcategory => container.GetService<SubcategoryView>(),
            _ => null
        };
    }
}

Notice how in this case, the Category runtime data is not supplied any longer to the constructor of the created views. This simplifies the creation of those objects, because their construction no longer depends on the existence of runtime data, which is a good practice. This also requires changing your ICategoryView.

  • Related