让我向您展示我在每个博客中看到的构建器设计模式实现:
interface IProductBuilder
{
void BuildPart1(Part1 value);
void BuildPart2(Part2 value);
void BuildPart3(Part3 value);
}
class ConcreteProduct
{
public readonly Part1 Part1;
public readonly Part2 Part2;
public readonly Part3 Part3;
public ConcreteProduct(Part1 part1, Part2 part2, Part3 part3)
{
Part1 = part1;
Part2 = part2;
Part3 = part3;
}
}
class ConcreteProductBuilder : IProductBuilder
{
Part1 _part1;
Part2 _part2;
Part3 _part3;
public void BuildPart1(Part1 value)
{
_part1 = value;
}
public void BuildPart2(Part2 value)
{
_part2 = value;
}
public void BuildPart3(Part3 value)
{
_part3 = value;
}
public ConcreteProduct GetResult()
{
return new ConcreteProduct(part1, part2, part3);
}
}
单元测试构建器的常用方法是这样的:
[TestMethod]
void TestBuilder()
{
var target = new ConcreteBuilder();
var part1 = new Part1();
var part2 = new Part2();
var part3 = new Part3();
target.BuildPart1(part1);
target.BuildPart2(part2);
target.BuildPart3(part3);
ConcreteProduct product = target.GetResult();
Assert.IsNotNull(product);
Assert.AreEqual(product.Part1, part1);
Assert.AreEqual(product.Part2, part2);
Assert.AreEqual(product.Part3, part3);
}
所以,这是一个非常简单的例子。
我认为建造者模式是一件非常好的事情。它使您能够将所有可变数据放在一个地方,并使所有其他类不可变,这对于可测试性来说很酷。
但是如果我不想将 Product 的字段暴露给某人(或者我不能这样做,因为 Product 是某个库的一部分)。
我应该如何对我的构建器进行单元测试?
现在会是这个样子吗?
[TestMethod]
void TestBuilder()
{
var target = new ConcreteProductBuilder();
var part1 = new Part1();
var part2 = new Part2();
var part3 = new Part3();
target.BuildPart1(part1);
target.BuildPart2(part2);
target.BuildPart3(part3);
ConcreteProduct product = target.GetResult();
TestConcreteProductBehaviorInUseCase1(product);
TestConcreteProductBehaviorInUseCase2(product);
...
TestConcreteProductBehaviorInUseCaseN(product);
}
在这里,我看到至少一个简单的解决方案-修改ConcreteProductBuilder.GetResult
为工厂:
public ConcreteProduct GetResult(IConcreteProductFactory factory)
{
return factory.Create(part1, part2, part3);
}
并IConcreteProductFactory
以两种方式实施:
public MockConcreteProductFactory
{
public Part1 Part1;
public Part2 Part2;
public Part3 Part3;
public ConcreteProduct Product;
public int Calls;
public ConcreteProduct Create(Part1 part1, Part2 part2, Part3 part3)
{
Calls++;
Part1 = part1;
Part2 = part2;
Part3 = part3;
Product = new ConcreteProduct(part1, part2, part3);
return Product;
}
}
public ConcreteProductFactory
{
public ConcreteProduct Create(Part1 part1, Part2 part2, Part3 part3)
{
return new ConcreteProduct(part1, part2, part3);
}
}
在这种情况下,测试将像以前一样简单:
[TestMethod]
void TestBuilder()
{
var target = new ConcreteBuilder();
var part1 = new Part1();
var part2 = new Part2();
var part3 = new Part3();
target.BuildPart1(part1);
target.BuildPart2(part2);
target.BuildPart3(part3);
var factory = new MockConcreteProductFactory();
ConcreteProduct product = target.GetResult(factory);
Assert.AreEqual(1, factory.Calls);
Assert.AreSame(factory.Product, product);
Assert.AreEqual(factory.Part1, part1);
Assert.AreEqual(factory.Part2, part2);
Assert.AreEqual(factory.Part3, part3);
}
所以我的问题不是关于如何以更好的方式解决它,而是关于 Builder 模式本身。
Builder 设计模式是否违反了单一职责原则?
对我来说,通用定义中的 builder 负责:
- 收集构造函数参数(或构建器模式的其他实现中的属性值)
- 使用收集的属性构造对象