Brandon Moore
Brandon Moore

Reputation: 8780

How to turn this Service Locator pattern into true Dependency Injection pattern?

I asked a more general question a minute ago: How to organize DI Framework usage in an application?, and the feedback I got was that I was using a Service Locator Pattern rather than true DI as is pointed out by Martin Fowler here: http://martinfowler.com/articles/injection.html

Actually, I read that article just the other day, but apparently haven't quite grasped it.

So let's say I have the following code:

interface ICardReader
{
    string GetInfo();
    void SetDebugMode(bool value);
    void Initialize(string accountToken);
    void ShowAmount(string amount);
    void Close();

    ICreditCardInfo GetCardInfo();
}

public class MagTekIPAD: ICardReader
{
    public ICreditCardInfo GetCardInfo()
    {
        var card = GetCardDataFromDevice();

        // apparently the following line is wrong?
        var ccInfo = Inject<ICreditCardInfo>.New(); 

        ccInfo.Track1 = MakeHex(card.EncTrack1);
        ccInfo.Track2 = MakeHex(card.EncTrack2);
        ccInfo.MagSignature = MakeHex(card.EncMP);
        ccInfo.MagSwipeKeySN = MakeHex(card.KSN);
        ccInfo.MagSignatureStatus = MakeHex(card.MPSts);
        ccInfo.MagDeviceSN = ipad.Serial;
        ccInfo.MSREncryptType = "MAGENSA_V5";

        return ccInfo;
    }

    // Other implementation details here ...
}

In this example I could inject the dependency into the constructor—and I think that's the correct way to fix 'this' scenario.

But what if I actually need to create an unknown number of the object in question (or are there any other legitimate reason I'd have a need to create the dependency on the fly in the class)?

Upvotes: 4

Views: 365

Answers (3)

Remo Gloor
Remo Gloor

Reputation: 32725

This example gives me the impression that you try to create a data transfer object namingly ICreditCardInfo using an IoC container. Such objects should not have any real dependencies like a service. The proper way to create DTOs is to use the new operator:

return new CreditCardInfo(
        MakeHex(card.EncTrack1),
        MakeHex(card.EncTrack2),
        MakeHex(card.EncMP),
        MakeHex(card.KSN),
        MakeHex(card.MPSts),
        ipad.Serial,
        "MAGENSA_V5");

Upvotes: 7

Sebastian Weber
Sebastian Weber

Reputation: 6806

Inject a factory for ICreditCardInfo objects into the constructor of MagTekIPAD

public class MagTekIPAD : ICardReader
{
  private readonly Func<ICreditCardInfo> factory;
  public MagTekIPAD(Func<ICreditCardInfo> factory)
  {
    this.factory = factory;
  }
  public ICreditCardInfo GetCardInfo()
  {
    var info = factory();
    // ...
    return info;
  }
}

Several containers can auto-generate Func<T> delegates if they know how to create instances of T so you don't have to define factory interfaces or abstract factory classes.

Upvotes: 3

Chui Tey
Chui Tey

Reputation: 5554

As Fowler pointed out, Service Locator is the more direct approach and is less error prone.

Some Dependency Injection frameworks require you to declare whether you are dealing with Singleton or they can have different lifetimes.

Upvotes: -1

Related Questions