Ответ 1
Ваша инверсия контейнера управления не является factory как таковой. Ваш случай идеально подходит для шаблона factory.
Создайте новый абстрактный factory, который используется для создания ваших монстров:
public interface IMonsterFactory
{
Zombie CreateZombie(string name);
Vampire CreateVampire(int age);
}
И затем зарегистрируйте его реализацию в Autofac.
Наконец, используйте factory в своем классе:
class Graveyard : ILocation
{
IMonsterFactory _monsterFactory;
public Graveyard(IMonsterFactory factory)
{
_monsterFactory = factory;
}
public void PresentLocalCreeps()
{
var vampire = _monsterFactory.CreateVampire(300);
vampire.IntroduceYourself();
var zombie = _monsterFactory.CreateZombie("Rob");
zombie.IntroduceYourself();
}
}
Конечно, вы можете использовать определенные фабрики монстров, если хотите. Тем не менее, используя интерфейсы, imho сделает ваш код намного более удобочитаемым.
Update
Но как бы реализовать factory? С одной стороны, factory не должен использовать контейнер IOC для создания монстров, потому что это считается злым (ухудшает шаблон DI для анти-шаблона локатора сервиса).
Я так устал слышать, что SL - это анти-шаблон. Не это. Как и во всех моделях, если вы используете его неправильно, это даст вам недостаток. Это относится ко всем образцам. http://blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern/
Но в этом случае я не понимаю, почему вы не можете создавать реализации непосредственно в factory? Для factory:
public class PreferZombiesMonsterFactory : IMonsterFactory
{
public Zombie CreateZombie(string name)
{
return new SuperAwesomeZombie(name);
}
public Vampire CreateVampire(int age)
{
return new BooringVampire(age);
}
}
Это не сложнее.
С другой стороны, factory не должен создавать самих монстров, потому что это обходит контейнер IOC и плотно соединяет factory и монстров. Или снова я ошибаюсь?;-)
Не имеет значения, что factory тесно связан с монстрами. Потому что цель factory: абстрагировать создание объекта, так что ничего другого в вашем коде не знает о бетонах.
Вы можете создать SuperDeluxeMonsterFactory
, MonstersForCheapNonPayingUsersFactory
и т.д. Все остальные коды в вашем приложении не будут знать, что вы используете разные монстры (используя разные фабрики).
Каждый раз, когда вам приходится менять бетоны, вы либо переключаете factory, либо просто изменяете существующий factory. Никакой другой код не будет затронут, пока ваши реализации монстров не будут нарушать Принцип замены Лискова.
Factory против контейнера IoC
Так что же тогда разница между factory и контейнером IoC? IoC отлично подходит для разрешения зависимостей для ваших классов и поддержания сроков службы (контейнер может, например, автоматически удалять все одноразовые устройства, когда заканчивается HTTP-запрос).
С другой стороны, factory выделяется при создании объектов для вас. Он делает это и ничего больше.
Резюме
Итак, если вы где-то в своем коде должны получить определенный тип реализации, вам обычно следует использовать factory. Сам factory МОЖЕТ использовать IoC как локатор службы внутри (для разрешения зависимостей). Это нормально, так как это деталь реализации в factory, которая не влияет ни на что другое в вашем приложении.
Используйте контейнер IoC (через инъекцию зависимостей), если вы хотите разрешить службу (и не заботятся о том, какую реализацию вы получаете или получаете ранее созданный экземпляр).