3

I have the following situation: a collection of objects have to be sent to different third parties based on a specific property of each object (the property is defined as an Enum). I intend to implement this using the Factory pattern like below.

Can this be refactored to use dependency injection instead?

public class ServiceA: IThirdParty
{
    public void Send(Ticket objectToBeSent)
    {
        // a call to the third party is made to send the ticket
    }
}

public class ServiceB: IThirdParty
{
    public void Send(Ticket objectToBeSent)
    {
        // a call to the third party is made to send the ticket
    }
}

public interface IThirdParty
{
    void Send(Ticket objectToBeSent);
}

public static class ThirdPartyFactory
{
    public static void SendIncident(Ticket objectToBeSent)
    {
        IThirdParty thirdPartyService = GetThirdPartyService(objectToBeSent.ThirdPartyId);
        thirdPartyService.Send(objectToBeSent);
    }

    private static IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return new ServiceA();

            default:
                return new ServiceB();
        }
    }
}
4
  • 2
    Why do you want to use dependency injection? Sounds like a homework task for some reason. Also: what have you tried so far? Commented May 15, 2013 at 12:57
  • DI and the factory pattern are not mutually exclusive. It's best to inject the factory into consumers that need it. Commented May 15, 2013 at 15:15
  • I've implemented the factory pattern as above but I was wandering if dependency injection would be more appropriate in this case. do you see any significant advantages from using DI? Commented May 16, 2013 at 6:33
  • @Anca - "do you see any significant advantages from using DI"? Yes, for example DI makes unit-testing easier. Commented May 20, 2013 at 7:57

5 Answers 5

2

Yes it can be refactored - inject the service into SendIncident, or its class.

Sign up to request clarification or add additional context in comments.

Comments

2

As far as I can tell, this question is about run-time selection or mapping of one of several candidate Strategies - in this case selecting the correct IThirdParty implementation based on a ThirdParty enum value.

There are at least three ways to do this in, and none of them require a factory:

My personal preference is the Partial Type Name Role Hint.

Comments

1

I think you should start with realizing that SendIncident should not be part of IThirdPartyFactory. Factories create objects. SendingIncidents is not that.

So introduce a class that is responsible for sending Tickets:

public TicketSender
{
    IThirdPartyFactory _factory

    public void TicketSender(IThirdPartyFactory factory)
    {
        _factory = factory;
    }


    public void SendIncident(Ticket ObjectToBeSent)
    {
         var service = _factory.GetThirdPartyService(ObjectToBeSent.ThirdPartyId);
         service.SendIncident(ObjectToBeSent);
    }
}

And use the following factory:

public class ThirdPartyFactory : IThirdPartyFactory
{
    IThirdParty serviceA;
    IThirdParty serviceB;

    public ThirdPartyFactory(IThirdParty serviceA, IThirdParty serviceB)
    {
        _serviceA = serviceA;
        _serviceB = serviceB;
    }

    public IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return serviceA;

            default:
                return serviceB;
        }
    }
}

If you are using an IOC container like Windsor, most of the factory will be generated for you, and you would only define the interface and the selection logic.

7 Comments

Thanks, I see your point, the "Single responsibility principle".
There is something that I don't understand. When injecting the services into the factory constructor, they both implement IThidParty, so how doe it know the order of the services?
It doesn't. THE constructor should take an IEnumerable<IThirdParty> services as an argument. THe GetTHirdPArty method then just does: return _services.FirstOrDefault(s=>s.Type==thirdParty) with maybe some checking for null and returning a default service if there's one. You need better naming though.
So both solutions provided are wrong. The alternative that you provided, with the collection, looks better but it would mean introducing some naming conventions. In this case wouldn't be better to stick to the factory pattern?
I assume you are want to use this in combinatation with an IOC container. Since serviceA and B seem to be different services and you can't find out from the service the Ids that it can handle (which I would prefer). I would not use a collection. Rather I would configure my container to resolve the two contstructor parameters to Service A and B. Are you using a specific IOC container ?
|
1

First, Marwijn is correct in that you should have a separate TicketSender class that takes an IThirdPartyFactory dependency.

You could create a new interface:

public interface IThirdPartyId
{
    int Id { get; }
}

Have all of your IThirdParty implementations also implement IThirdPartyId, by convention. Then you can inject a collection of IThirdParty into your ThirdPartyFactory as Marwijn suggested. The Factory can recast them as IThirdPartyId to get the Id of each, and use that to match on the Id parameter on the factory method.

This would keep all of your lookup logic in the factory and not split between the factory and your IoC configuration.

Comments

0

Why not? Just tweak the ThirdPartyFactory a bit to become Abstract Factory, and don't use static.

public class ThirdPartyFactory : IThirdPartyFactory
{
    public ThirdPartyFactory(IThirdParty serviceA, IThirdParty serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }
    IThirdParty serviceA;
    IThirdParty serviceB;

    public void SendIncident(Ticket objectToBeSent)
    {
        IThirdParty thirdPartyService = GetThirdPartyService(objectToBeSent.ThirdPartyId);
        thirdPartyService.SendIncident(objectToBeSent);
    }

    private IThirdParty GetThirdPartyService(ThirdParty thirdParty)
    {
        switch (thirdParty)
        {
            case ThirdParty.AAA:
                return serviceA;

            default:
                return serviceB;
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.