开发者

Dependency inversion. Object creation

开发者 https://www.devze.com 2023-03-30 19:05 出处:网络
According to SOLID principles a class cannot depend on other classes, dependencies have to be injected. It\'s simple:

According to SOLID principles a class cannot depend on other classes, dependencies have to be injected. It's simple:

class Foo
{
    public Foo(IBar bar)
    {
        this.bar = bar;
    }

    private IBar bar;
}

interface IBar 
{
}

class Bar: IBar 
{
}

But what if I want my Foo class to be able to create Bar's, not knowing the exact implementation behind IBar? I can think of 4 solutions here, but all of them seem to have drawbacks:

  1. injecting the type of object and using reflection
  2. using Generics
  3. using "Service Locator" and calling the Resolve() method.
  4. creating a separated factory class and injecting it into Foo:

class Foo
{
    public void DoSmth(IBarCreator barCreator) 
    {
        var newBar = barCreator.CreateBar();
    }
}

interface IBarCrea开发者_如何学Ctor 
{
    IBar CreateBar();
}

class BarCreator : IBarCreator
{
    public IBar CreateBar()
    {
        return new Bar();
    }
}

Last case seems natural, but BarCreator class has too litle code. So how do you think, which is best?


I like to "inject" a Func<IBar> in this case. Like so:

class Foo
{
    public Foo(Func<IBar> barCreator)
    {
        this.bar = barCreator();
    }

    private IBar bar;
}

interface IBar 
{
}


This is what factories were made for.

If you feel your factory has too little code, ask yourself what benefit it's giving you over just creating the instance inline. If the benefits outweigh the costs of the added code, then don't worry about it.

I'd personally avoid service location, or if you really must use it I'd hide it behind a factory anyway. Service location tends to be easily abused, and can lead to your container finding its way into code it shouldn't have anything to do with.

For convenience, some containers allow you to specify a factory to be used by the container when it creates an instance of a component. In this case, your class could depend on IBar directly but your container would call IBarCreator when it needs a new instance. Castle Windsor has the methods UseFactory and UseFactoryMethod in their API, for example.


it all depends on your exact scenario and your needs.
I think the most used approach is, as you've mentioned, a factory.
If you're using an IoC framework (such as Ninject, or Sprint.Net, Castle Windsor etc. see here), a service locator is also a viable solution.


If you have a problem with redundant factory interface, you can take two approaches here.

Make it reusable with generics:

interface IFactory<T>
{
 T Create();
}

class DefaultConstructorFactory<T> : IFactory<T>, where T: new()
{
 public T Create() { return new T();}
}

Or use anonymous function as a factory:

public void DoSomething(Func<IBar> barCreator)
{
 var newBar = barCreator();
 //...
}


I feel when you say "I want my Foo class to be able to create Bar's" one more rule comes to play that is "Separation of Concerns". So you should delegate the task of class creation to something else and Foo should not be worried about that task.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号