正确使用静态工厂方法创建具有预定义值的 DTO 对象

The proper usage of a static factory method for creating a DTO object with predefined values

假设我们必须创建具有一些预定义(默认)值的复杂 DTO 对象。此对象用于序列化,序列化程序需要无参数构造函数。为此,我想使用静态工厂方法,但我对这种方法的正确使用有一些疑问。

请考虑以下两个例子:

public class Foo
{
    public void DoSomething()
    {
        // the first way of creating the object
        var addressDtoFirstWay = AddressDtoFirstWay
            .CreateWithPredefinedValues();
        addressDtoFirstWay.StreetName = "Street";
        addressDtoFirstWay.HouseNumber = 100;
        addressDtoFirstWay.PostalCode = "1000";

        // the second way of creating the object
        var addressDtoSecondWay = AddressDtoSecondWay
            .CreateWithPredefinedValues("Street", 100, null, "1000");
    }
}

public class AddressDtoFirstWay
{
    public string RecipientName { get; set; }
    public string StreetName { get; set; }
    public int HouseNumber { get; set; }
    public int? FlatNumber { get; set; }
    public string PostalCode { get; set; }
    public string City { get; set; }
    public string CountryName { get; set; }

    public static AddressDtoFirstWay CreateWithPredefinedValues()
    {
        return new AddressDtoFirstWay
        {
            RecipientName = "John Doe",
            City = "City",
            CountryName = "Country"
        };
    }
}

public class AddressDtoSecondWay
{
    public string RecipientName { get; set; }
    public string StreetName { get; set; }
    public int HouseNumber { get; set; }
    public int? FlatNumber { get; set; }
    public string PostalCode { get; set; }
    public string City { get; set; }
    public string CountryName { get; set; }

    public static AddressDtoSecondWay CreateWithPredefinedValues(
        string streetName,
        int houseNumber,
        int? flatNumber,
        string postalCode)
    {
        return new AddressDtoSecondWay
        {
            RecipientName = "John Doe",
            StreetName = streetName,
            HouseNumber = houseNumber,
            FlatNumber = flatNumber,
            PostalCode = postalCode,
            City = "City",
            CountryName = "Country"
        };
    }
}

在第一个示例中,工厂方法仅初始化预定义字段 - 用户必须在创建对象后初始化其余字段。第二个示例初始化预定义字段,也是必需的字段,但不利的是,用户必须填写可空(在这种情况下不需要,但在其他情况下需要)字段 flatNumber.

我看到了这两种解决方案的优点和缺点,但我正在考虑更喜欢哪一种以及为什么。也许其他一些方法会更好。我乐于接受任何建议,但我想指出,构建器模式适用的问题并不复杂。

假设你

  • 想要创建具有特定默认值的 DTO 的方法
  • 想要默认构造函数,不想强制使用工厂方法

也许答案是将两者分开。让你的 DTO 做自己的事:

public class AddressDto
{
    public string RecipientName { get; set; }
    public string StreetName { get; set; }
    public int HouseNumber { get; set; }
    public int? FlatNumber { get; set; }
    public string PostalCode { get; set; }
    public string City { get; set; }
    public string CountryName { get; set; }
}

...并且不要将其与各种默认选项混为一谈。随着时间的推移,您会发​​现您需要针对各种场景使用不同的默认值。我可以看到变得有点凌乱。

然后,将那些完全相同的静态方法放入它们自己的 class:

public static class AddressDtoFactory
{
    public static AddressDto CreateWithPredefinedValues()
    {
        return new AddressDto
        {
            RecipientName = "John Doe",
            City = "City",
            CountryName = "Country"
        };
    }
}

我倾向于 "first way",其中工厂方法仅填充默认值。原因是必须将每个 属性 作为参数传递,这有点麻烦,尤其是在它们没有得到验证的情况下。另外,每次添加 属性 时,您都希望更新该构造函数。

另一种选择是扩展 class,如下所示:

public static class AddressDtoExtensions
{
    public static AddressDto PopulatePredefinedValues(
        this AddressDto dto)
    {
        dto.RecipientName = dto.RecipientName ?? "John Doe";
        dto.City = dto.City ?? "City";
        dto.CountryName = dto.CountryName ?? "Country";
        return dto;
    }
}

这让你可以做这样的事情:

var dto = new AddressDto
{
    HouseNumber = 5,
    PostalCode = "12345"
}.PopulatePredefinedValues();

它为您提供了两者 - 您可以同时使用 属性 初始化和添加默认值。如果您决定使用 Automapper 或类似的东西,它也可能会更友好一些。