У меня есть следующий сценарий:
public void DoSomething(...)
{
...
ClassA obj1 = new ClassA();
ClassB obj2 = new ClassB();
ClassC obj3 = new ClassC();
...
}
Я понимаю, что если бы я использовал принцип инверсии зависимостей, я мог бы размахивать интерфейсом, и он будет работать. Проблема в том, что эти экземпляры необходимы только в этом методе, поэтому нет смысла создавать их на уровне класса. Кроме того, некоторые методы имеют 5-6 объявлений объектов, поэтому передача этих параметров будет раздувать список параметров.
Есть ли в любом случае я могу использовать Moq, NMock и т.д. Для моделирования этих классов (которые основаны на общем интерфейсе)?
Если вам нужно только создать эти типы в этом методе, вы правы в том, что их не следует вводить в класс.
Вместо этого типичным решением является внедрение фабрики, которая позволит вашему методу разрешить интерфейс.
Здесь обычный подход:
interface IMessageFactory
{
IMessage CreateMessage(string text);
}
class ConcreteMessageFactory : IMessageFactory
{
IMessage CreateMessage(string text) { return new Message(text); }
}
class MockMessageFactory : IMessageFactory
{
public IMessage CreateMessage(string text) { return new MockMessage(text); }
}
class MyClient
{
private readonly IMessageFactory _msgFactory;
public MyClient(IMessageFactory msgFactory)
{
_msgFactory = msgFactory;
}
public void SendMessage(string text)
{
IMessage msg = _msgFactory.CreateMessage(text);
msg.Send();
}
}
//Usage:
new MyClient(new ConcreteMessageFactory());
//Mocking
new MyClient(new MockMessageFactory());
В зависимости от того, какой каркас DI вы используете, инфраструктура может сделать этот способ более удобным для вас. Например, Castle Windsor позволяет вам вводить фабрику на основе делегата, в отличие от фабрики на основе интерфейса:
class MyClient
{
private readonly Func<string, IMessage> _msgFactory;
public MyClient(Func<string, IMessage> msgFactory)
{
_msgFactory = msgFactory;
}
public void SendMessage(string text)
{
IMessage msg = _msgFactory(text);
msg.Send();
}
}
//Usage:
new MyClient(() => new Message());
//Mocking
new MyClient(() => new MockMessage());
Вы можете использовать что-то вроде StructureMap для выполнения вашей работы, ваш метод станет чем-то вроде.
public void DoSomething(...)
{
...
IClassA obj1 = ObjectFactory.GetInstance<IClassA>();
IClassB obj2 = ObjectFactory.GetInstance<IClassB>();
IClassC obj3 = ObjectFactory.GetInstance<IClassC>();
...
}
Тогда, как правило, вы должны сопоставлять их как в global.asax, так и в аналогичных
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<ClassA>();
...etc
});
Но для ваших модульных тестов
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<UnitTestClassA>();
...etc
});