Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 204
Brandon Moore Avatar asked Jan 18 '23 10:01

Brandon Moore


2 Answers

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");
like image 194
Remo Gloor Avatar answered Jan 22 '23 21:01

Remo Gloor


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.

like image 20
Sebastian Weber Avatar answered Jan 22 '23 20:01

Sebastian Weber