对于测试,我有Factory
使用Product
生成Builder
。每个Product
都可以具有状态(Available
/ InUse
/ Disposed
/等)。我需要生产各种状态的产品。
我的问题在于,为了让我生成一个Product
,让我们说Disposed
状态,首先需要InUse
(必须创建它)使用new ProductBuilder().CheckIn().Dispose().Build();
而不仅仅是new ProductBuilder().Dispose().Build();
)
我如何(或必须我?)强制执行构建器方法的前提条件并保持圈复杂度为1 (因此它不需要进一步测试)。
我不希望使用类似if (product.Status == Product.INUSE) {...}
的内容并为每种可能的场景投掷exceptions
(不同的状态需要不同的前置条件)。
由于构建器是私有的,我是否需要强制执行此操作?我只是依靠程序员来了解需要调用方法的顺序,并在每个构建器方法之前添加一些注释?我选择不同的模式(哪个?)。
public static class ProductFactory
{
private class ProductBuilder
{
private Product product;
public ProductBuilder()
{
product = new Product {Status = product.AVAILABLE};
}
public ProductBuilder Dispose()
{
product.Status = product.DISPOSED; return this;
}
public ProductBuilder CheckIn()
{
product.Status = product.INUSE; return this;
}
public Product Build()
{
return product;
}
}
public static Product CreateAvailable()
{
return new ProductBuilder().Build();
}
public static Product CreateInUse()
{
return new ProductBuilder().CheckIn().Build();
}
public static Product CreateDisposed()
{
return new ProductBuilder().CheckIn().Dispose().Build();
}
}
答案 0 :(得分:3)
首先,您必须跨多个界面隔离这些方法(CheckIn
和Disposed
):
public interface IBaseBuilder
{
Product Build();
}
public interface IProductBuilder : IBaseBuilder
{
ICheckedInProductBuilder CheckIn();
}
public interface ICheckedInProductBuilder : IBaseBuilder
{
IDisposedProductBuilder Dispose();
}
public interface IDisposedProductBuilder : IBaseBuilder
{
}
这样,给定一个初始IProductBuilder
:
CheckIn
- >按特定顺序Dispose
CheckIn
后,无法再次调用Dispose
后,无法再次调用Build
来创建产品。为了使事情更容易实现,您可以让一个类实现所有三个接口,并使用主IProductBuilder
接口将其注入其客户端。或者,您可以使用不同的类来实现接口。我会把它留作练习。
作为一个真实世界的例子,这种技术在Moq和FluentAssertions中被广泛使用,以实现流畅的API。
答案 1 :(得分:2)
您可以通过返回接口来抽象实现,而不是返回ProductBuilder
。这样,您可以通过首先接收该接口来使某些方法可用。
让我在代码中解释一下。假设Disposed
功能仅在InUse
之后才可用,那么您可以这样做:
public interface IInUseProductBuilder
{
IDisposedProductBuilder Dispose();
}
public interface IDisposedProductBuilder
{
IBuiltProductBuilder Build();
}
当你实施它时:
public class ProductBuilder : IInUseProductBuilder, IDisposedProductBuilder
{
public IDisposedProductBuiler Dispose()
{
product.Status = product.DISPOSED;
return this;
}
}
这样,用户只能使用在特定构建器上声明的方法,您将根据工作流程公开这些方法。