Question

I'm struggling to understand parts of StructureMap's usage. In particular, in the documentation a statement is made regarding a common anti-pattern, the use of StructureMap as a Service Locator only instead of constructor injection (code samples straight from Structuremap documentation):

 public ShippingScreenPresenter()
    {
        _service = ObjectFactory.GetInstance<IShippingService>();
        _repository = ObjectFactory.GetInstance<IRepository>();
    }

instead of:

public ShippingScreenPresenter(IShippingService service, IRepository repository)
    {
        _service = service;
        _repository = repository;
    }

This is fine for a very short object graph, but when dealing with objects many levels deep, does this imply that you should pass down all the dependencies required by the deeper objects right from the top? Surely this breaks encapsulation and exposes too much information about the implementation of deeper objects.

Let's say I'm using the Active Record pattern, so my record needs access to a data repository to be able to save and load itself. If this record is loaded inside an object, does that object call ObjectFactory.CreateInstance() and pass it into the active record's constructor? What if that object is inside another object. Does it take the IRepository in as its own parameter from further up? That would expose to the parent object the fact that we're access the data repository at this point, something the outer object probably shouldn't know.

public class OuterClass
{
    public OuterClass(IRepository repository)
    {
        // Why should I know that ThingThatNeedsRecord needs a repository?
        // that smells like exposed implementation to me, especially since
        // ThingThatNeedsRecord doesn't use the repo itself, but passes it 
        // to the record.
        // Also where do I create repository? Have to instantiate it somewhere
        // up the chain of objects
        ThingThatNeedsRecord thing = new ThingThatNeedsRecord(repository);
        thing.GetAnswer("question");
    }
}

public class ThingThatNeedsRecord
{
    public ThingThatNeedsRecord(IRepository repository)
    {
        this.repository = repository;
    }

    public string GetAnswer(string someParam)
    {
        // create activeRecord(s) and process, returning some result
        // part of which contains:
        ActiveRecord record = new ActiveRecord(repository, key);
    }

    private IRepository repository;
}

public class ActiveRecord
{
    public ActiveRecord(IRepository repository)
    {
        this.repository = repository;
    }

    public ActiveRecord(IRepository repository, int primaryKey);
    {
        this.repositry = repository;
        Load(primaryKey);
    }

    public void Save();

    private void Load(int primaryKey)
    {
        this.primaryKey = primaryKey;
        // access the database via the repository and set someData
    }

    private IRepository repository;
    private int primaryKey;
    private string someData;
}

Any thoughts would be appreciated.

Simon

EDIT: Opinion seems to be that the injection should start at the top layer. ActiveRecord would be injected into ThingThatNeedsRecord which is injected into OuterClass. The problem with this would be that if ActiveRecord needs to be instantiated with a run-time parameter (the id of the record to retrieve for example). If I'm injecting ActiveRecord into ThingThatNeedsRecord right at the top, I somehow have to figure out what id needs to be at that point (which exposes the top layer to implementation which it shouldn't) or I have to have a partially constructed ActiveRecord and set the Id later on. This becomes more complicated if I need N records and won't know until execution of logic inside ThingThatNeedsRecord.

Was it helpful?

Solution

Inversion of Control is like violence. If it doesn't solve your problem, you're not using enough of it. Or something like that.

More to the point, I think your OuterClass should have ThingThatNeedsRecord injected into it via constructor injection. Likewise ThingThatNeedsRecord should have ActiveRecord injected into it. Not only will this solve your immediate problem, but it will make your code more modular and testable as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top