У меня нет большого опыта работы с шаблоном factory, и я столкнулся с сценарием, в котором я считаю, что это необходимо, но я не уверен, что правильно реализовал шаблон, и я обеспокоенный тем, какое влияние он оказал на читаемость моих модульных тестов.
Я создал фрагмент кода, который аппроксимирует (из памяти) суть сценария, над которым я работаю на работе. Я был бы очень признателен, если бы кто-то мог взглянуть на него и посмотреть, не кажется ли мне, что я сделал, разумно.
Это класс, который мне нужно проверить:
public class SomeCalculator : ICalculateSomething
{
private readonly IReducerFactory reducerFactory;
private IReducer reducer;
public SomeCalculator(IReducerFactory reducerFactory)
{
this.reducerFactory = reducerFactory;
}
public SomeCalculator() : this(new ReducerFactory()){}
public decimal Calculate(SomeObject so)
{
reducer = reducerFactory.Create(so.CalculationMethod);
decimal calculatedAmount = so.Amount * so.Amount;
return reducer.Reduce(so, calculatedAmount);
}
}
Вот некоторые из основных понятий интерфейса...
public interface ICalculateSomething
{
decimal Calculate(SomeObject so);
}
public interface IReducerFactory
{
IReducer Create(CalculationMethod cm);
}
public interface IReducer
{
decimal Reduce(SomeObject so, decimal amount);
}
Это factory, который я создал. Мои текущие требования заставили меня добавить конкретный редуктор MethodAReducer для использования в конкретном сценарии, поэтому я пытаюсь ввести factory.
public class ReducerFactory : IReducerFactory
{
public IReducer Create(CalculationMethod cm)
{
switch(cm.Method)
{
case CalculationMethod.MethodA:
return new MethodAReducer();
break;
default:
return DefaultMethodReducer();
break;
}
}
}
Это аппроксимации двух реализаций... Сущность реализации заключается в том, что она только уменьшает количество, если объект находится в определенном состоянии.
public class MethodAReducer : IReducer
{
public decimal Reduce(SomeObject so, decimal amount)
{
if(so.isReductionApplicable())
{
return so.Amount-5;
}
return amount;
}
}
public class DefaultMethodReducer : IReducer
{
public decimal Reduce(SomeObject so, decimal amount)
{
if(so.isReductionApplicable())
{
return so.Amount--;
}
return amount;
}
}
Это тестовое оборудование, которое я использую. Меня беспокоило, сколько места в тестах рассмотрено шаблоном factory и как оно уменьшает читаемость теста. Пожалуйста, имейте в виду, что в моем классе реального мира у меня есть несколько зависимостей, которые мне нужно высмеять, что означает, что тесты здесь на несколько строк короче, чем те, которые необходимы для моего реального теста.
[TestFixture]
public class SomeCalculatorTests
{
private Mock<IReducerFactory> reducerFactory;
private SomeCalculator someCalculator;
[Setup]
public void Setup()
{
reducerFactory = new Mock<IReducerFactory>();
someCalculator = new SomeCalculator(reducerFactory.Object);
}
[Teardown]
public void Teardown(){}
Первый тест
//verify that we can calculate an amount
[Test]
public void Calculate_CalculateTheAmount_ReturnsTheAmount()
{
decimal amount = 10;
decimal expectedAmount = 100;
SomeObject so = new SomeObjectBuilder()
.WithCalculationMethod(new CalculationMethodBuilder())
.WithAmount(amount);
Mock<IReducer> reducer = new Mock<IReducer>();
reducer
.Setup(p => p.Reduce(so, expectedAmount))
.Returns(expectedAmount);
reducerFactory
.Setup(p => p.Create(It.IsAny<CalculationMethod>))
.Returns(reducer);
decimal actualAmount = someCalculator.Calculate(so);
Assert.That(actualAmount, Is.EqualTo(expectedAmount));
}
Второй тест
//Verify that we make the call to reduce the calculated amount
[Test]
public void Calculate_CalculateTheAmount_ReducesTheAmount()
{
decimal amount = 10;
decimal expectedAmount = 100;
SomeObject so = new SomeObjectBuilder()
.WithCalculationMethod(new CalculationMethodBuilder())
.WithAmount(amount);
Mock<IReducer> reducer = new Mock<IReducer>();
reducer
.Setup(p => p.Reduce(so, expectedAmount))
.Returns(expectedAmount);
reducerFactory
.Setup(p => p.Create(It.IsAny<CalculationMethod>))
.Returns(reducer);
decimal actualAmount = someCalculator.Calculate(so);
reducer.Verify(p => p.Reduce(so, expectedAmount), Times.Once());
}
}
Так все выглядит правильно? Или есть лучший способ использовать шаблон factory?