Reputation: 413
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.
Upvotes: 1
Views: 2793
Reputation: 12077
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.
Upvotes: 6