Skip to main content
Commonmark migration
Source Link

#Service Locator Alert!!

Service Locator Alert!!

Don't call them, they'll call you!

 

(don't call into the IoC container, the IoC container will call into your constructor)

#Service Locator Alert!!

Don't call them, they'll call you!

 

(don't call into the IoC container, the IoC container will call into your constructor)

Service Locator Alert!!

Don't call them, they'll call you!

(don't call into the IoC container, the IoC container will call into your constructor)

cleaned up abstract factory example
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463

Wiring this up with the IoC container is going to be tricky, but it could be simplified if the ISetup was injected in a factory class' constructor - first we need an abstraction for the Hub:

public interface IHub
{
    void Start();
}

Now let's create a factory class whose sole responsibility is to create IHub instances:

public class SetupHubFactory
{
    private readonly ISetup _setup;

    public SetupHubFactory(ISetup setup)
    {
        _setup = setup;
    }

    public SetupHubIHub Create(Action<double> updateProgress)
    {
        return new SetupHub(_setup, updateProgress);
    }
}
public class SetupHubFactory : IHubFactory<SetupHub>IHubFactory
{
    // ...
} 

public interface IHubFactory<THub> where THub : HubIHubFactory
{
    THubIHub Create(action<double> updateProgress);
}

This IHubFactory<THub>IHubFactory is an abstract factory (a generic one) - the client code receives any HubFactory, and works against an abstraction, instead of being tied to a single specific implementation:

public class ClientObject
{
    private readonly IHubFactory _hubFactory;

    public ClientObject(IHubFactory hubFactory)
    {
        _hubFactory = hubFactory;
    }

    public void DoSomething()
    {
        // hub is of type IHub - ClientObject isn't tied to a specific implementation.
        // inject a new implementation of the `IHubFactory` and no changes needed,
        // this method will create an instance of the new hub implementation:
        var hub = _hubFactory.Create(UpdateProgressBar);
        hub.Start();
    }

    private void UpdateProgressBar(double progress)
    {
        // ...
    }
}

Wiring this up with the IoC container is going to be tricky, but it could be simplified if the ISetup was injected in a factory class' constructor:

public class SetupHubFactory
{
    private readonly ISetup _setup;

    public SetupHubFactory(ISetup setup)
    {
        _setup = setup;
    }

    public SetupHub Create(Action<double> updateProgress)
    {
        return new SetupHub(_setup, updateProgress);
    }
}
public class SetupHubFactory : IHubFactory<SetupHub>
{
    // ...
} 

public interface IHubFactory<THub> where THub : Hub
{
    THub Create(action<double> updateProgress);
}

This IHubFactory<THub> is an abstract factory (a generic one) - the client code receives any HubFactory, and works against an abstraction, instead of being tied to a single specific implementation:

public class ClientObject
{
    private readonly IHubFactory _hubFactory;

    public ClientObject(IHubFactory hubFactory)
    {
        _hubFactory = hubFactory;
    }

    public void DoSomething()
    {
        var hub = _hubFactory.Create(UpdateProgressBar);
        hub.Start();
    }

    private void UpdateProgressBar(double progress)
    {
        // ...
    }
}

Wiring this up with the IoC container is going to be tricky, but it could be simplified if the ISetup was injected in a factory class' constructor - first we need an abstraction for the Hub:

public interface IHub
{
    void Start();
}

Now let's create a factory class whose sole responsibility is to create IHub instances:

public class SetupHubFactory
{
    private readonly ISetup _setup;

    public SetupHubFactory(ISetup setup)
    {
        _setup = setup;
    }

    public IHub Create(Action<double> updateProgress)
    {
        return new SetupHub(_setup, updateProgress);
    }
}
public class SetupHubFactory : IHubFactory
{
    // ...
} 

public interface IHubFactory
{
    IHub Create(action<double> updateProgress);
}

This IHubFactory is an abstract factory - the client code receives any HubFactory, and works against an abstraction, instead of being tied to a single specific implementation:

public class ClientObject
{
    private readonly IHubFactory _hubFactory;

    public ClientObject(IHubFactory hubFactory)
    {
        _hubFactory = hubFactory;
    }

    public void DoSomething()
    {
        // hub is of type IHub - ClientObject isn't tied to a specific implementation.
        // inject a new implementation of the `IHubFactory` and no changes needed,
        // this method will create an instance of the new hub implementation:
        var hub = _hubFactory.Create(UpdateProgressBar);
        hub.Start();
    }

    private void UpdateProgressBar(double progress)
    {
        // ...
    }
}
fleshed up
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463
public void Run()
{
    // 1. Instantiate your favorite IoC container.
    // 2. Configure your favorite IoC container.
    // 3. Resolve the entire app's dependency graph at once.
 
    // 4. Profit: // 
    var client = _ioc.GetInstance<ClientObject>();
    client.DoSomething();

    /*  */

    // or..
    // go with Poor Man's DI if you don't have a favorite IoC container:
    var setup = new Setup();
    var factory = new SetupHubFactory(setup);
    var client = new ClientObject(factory);

    client.DoSomething();
}

Bottom lineIt's your IoC container's job to new up the dependencies and automagically inject them into all the required constructors when you ask for a ClientObject ("ask" is important here). It works automagically because all the classes involved in the dependency graph tell exactly everything they need ("tell" is important here); only infrastructure code directly works with the IoC container, the rest of the application is blissfully unaware of its existence.

Tell, don't ask.

Also, with DI/IoC you need to follow the Hollywood Principle:

Don't call them, they'll call you!

(don't call into the IoC container, the IoC container will call into your constructor)

public void Run()
{
    // 1. Instantiate your favorite IoC container.
    // 2. Configure your favorite IoC container.
    // 3. Resolve the entire app's dependency graph at once.

    // or go with Poor Man's DI if you don't have a favorite IoC container:
    var setup = new Setup();
    var factory = new SetupHubFactory(setup);
    var client = new ClientObject(factory);

    client.DoSomething();
}

Bottom line, with DI/IoC you need to follow the Hollywood Principle:

Don't call them, they'll call you!

public void Run()
{
    // 1. Instantiate your favorite IoC container.
    // 2. Configure your favorite IoC container.
    // 3. Resolve the entire app's dependency graph at once.
 
    // 4. Profit: // 
    var client = _ioc.GetInstance<ClientObject>();
    client.DoSomething();

    /*  */

    // or..
    // go with Poor Man's DI if you don't have a favorite IoC container:
    var setup = new Setup();
    var factory = new SetupHubFactory(setup);
    var client = new ClientObject(factory);

    client.DoSomething();
}

It's your IoC container's job to new up the dependencies and automagically inject them into all the required constructors when you ask for a ClientObject ("ask" is important here). It works automagically because all the classes involved in the dependency graph tell exactly everything they need ("tell" is important here); only infrastructure code directly works with the IoC container, the rest of the application is blissfully unaware of its existence.

Tell, don't ask.

Also, with DI/IoC you need to follow the Hollywood Principle:

Don't call them, they'll call you!

(don't call into the IoC container, the IoC container will call into your constructor)

fleshed up
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463
Loading
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463
Loading