让我向您展示每个博客中都看到的构建器设计模式实现:
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);
}
所以,这是一个相当简单的例子。
我认为建造者模式是一件非常好的事情。它使您能够将所有可变数据放置在一个地方,并使所有其他类保持不可变,这对于可测试性非常酷。
但是,如果我不想向某个人公开产品的字段(或者我只是不能这样做,因为产品是某个库的一部分),该怎么办呢?
我应该如何对我的构建器进行单元测试?
现在它会是这个样子吗?
[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);
}
我的问题不是如何更好地解决它,而是关于建造者模式本身。
建造者设计模式是否违反单一职责原则?
对我来说,通常定义下的建造者负责以下两个方面:
- 收集构造函数参数(或其他实现中的属性值)
- 使用收集到的属性构造对象