首先重新检查域的 StepsProgression:如果我们向域专家询问我们的流程步骤,我认为我们会得到一个可以接受的答案。那么我们可以说这是我们领域的一部分。
接下来,正如你所说,这个实体还是 VO?是的,它看起来像值对象,实际上是一个值对象,因为您不关心整个数据集的身份,它没有唯一的含义(如果我没有理解 domaing 错误的话)。
假设你有一个类似表单向导的用户界面,并且每一步的输入都不同,我会这样实现:
public class StepsProgression
{
private readonly string _userId;
public Step1 step1 { get; set; }
public Step2 step2 { get; set; }
public Step3 step3 { get; set; }
public StepsProgression(string userId)
{
_userId = userId;
}
}
// Immutable Step Object
public class Step1
{
public string input1 { get; }
public string input2 { get; }
public string input3 { get; }
}
您在第一步中实例化了 StepsProgression,所以我假设您只知道 谁的 个步骤。所以构造函数只设置用户 ID。然后当用户填写 step1 输入并单击 Next 时,您将创建一个新的(不可变的)Step 对象并将其设置为 step1。如果用户单击Back,更改 Step1 中的任何内容并再次单击 Next,则您再次创建一个新的 Step1 对象并将其分配给 StepsProgression。
您可以考虑更通用的实现(步骤),但由于步骤之间的输入都不同,这种显式方式为您提供了编码约定。
编辑的实现:
我了解 StepsProgressions 包含您在创建 StepsProgression 之前确定的一组动态步骤。然后假设你事先知道 stepTypes 我会改变我的实现如下:
public class StepsProgression
{
private readonly string _userId;
private readonly IStepProgressionBuilder _builder = new StepProgressionBuilder();
public IEnumerable<IStep> Steps { get; set; }
public StepsProgression(string userId, IEnumerable<string> stepTypes)
{
_userId = userId;
foreach (var step in stepTypes) // foreach smells here but you got the point
{
_builder.AddConcreteStep(step) // step = "Step1" for instance.
}
Steps = _builder.Build();
}
}
// Immutable Step Object
public class Step1 : IStep
{
public string input1 { get; }
public string input2 { get; }
public string input3 { get; }
}
// Builder Pattern with Fluent Methods
public interface IStepProgressionBuilder
{
// stepType = "step1" for instance. You have concrete Step1 class, so you return it.
// Start with a switch, then you refactor. (May involve some reflection)
void AddConcreteStep(string stepType);
IEnumerable<IStep> Build();
}
同样,您可以使用一个通用 Step 类,但是您的构建器应该展示如何通过获取 Step 应包含的每个数据来构建 Step。如果您的步骤可能具有 3 个以上的属性,则这将成为代码气味。这就是为什么我更喜欢使用具体的 Step 类,以便我可以将其构建为一个完整的数据集。