参数对象的构建器模式

时间:2018-12-23 17:34:07

标签: oop design-patterns refactoring

请原谅我对设计模式的了解。

有时候方法有很多参数,引入 Parameter Object 是根据refactoring-guru article重构代码的正确方法。

想象一下当我们有一个处理某种文档的服务的情况。

public class FinancialStatementService : IFinancialStatementService
{
    public Document Create(Options input)
    {
        // creating basic document content and adding optional data below:

        if (input.HasAccountNo)
        {
            // include account number
        }
        if (input.HasPaymentDetails)
        {
            // include payment details
        }
        if (input.HasZeroBalances)
        {
            // include zero balances
        }
        if (input.HasTotal)
        {
            // include total
        }

        // and then return the document
    }
}


public class Options
{
    public DateRange DateRange { get; set; }

    public bool HasAccountNo { get; set; }
    public bool HasPaymentDetails { get; set; }
    public bool HasZeroBalances { get; set; }
    public bool HasTotal { get; set; }
}

文档由许多部分组成,有些是可选的。有时我们需要文档包含所有可能的详细信息。

但是,假设某个组织不想要某些细节的情况。

理想情况下,我希望有一个处理选项创建的类,并具有包含组织名称的方法,例如

public class OptionsCreator
{
    // tax officials want to see all possible details
    public static Options GetTaxOfficeOptions(DateRange dateRange)
    {
        return new Options() { HasAccountNo = true, HasPaymentDetails = true, HasZeroBalances = true, HasTotal = true, DateRange = dateRange };
    }

    // in some other organization they DO NOT NEED zero balances & payment details
    public static Options GetSomeOtherOgranizationOptions(DateRange dateRange)
    {
        return new Options() { HasAccountNo = true, HasTotal = true, DateRange = dateRange };
    }
}

但是我怕上面的例子是反模式的。

我能想到的另一件事是构建器模式。

构建器模式是否是参数对象的最佳解决方案?

2 个答案:

答案 0 :(得分:1)

我通常这样使用smth:

  1. 带有-的常量类(在此示例中,我将第一个参数用于字符串以进行xml解析,第二个参数用作默认值) public static readonly Dictionary<string, Tuple<string, object>> fieldNamesWithOptions = new Dictionary<string, Tuple<string, object>>() { { "number", new Tuple<string, object>("PONumber","") }, { "shipDate", new Tuple<string, object>("ShipDate", new DateTime(1900, 1, 1)) }, { "detailID", new Tuple<string, object>("DetailID", -1) }, }

  2. 像这样处理您的选项(伪代码,假设Tuple中的第三个参数正在准备函数):


    for every field F in object O:
        if(fieldNamesWithOptions.ContainsKey(F.name)) 
        {
            result += fieldNamesWithOptions[F].third(O.F);
        }

我使用伪代码,因为在C#中它将看起来相当复杂。

此重构将清除您的代码,将逻辑分成可以理解/测试的小片段。另一方面,我们有一个异常的周期for every field F in object O:,但只需调试一次,就可以在项目的任何其他部分的帮助程序中使用它。

答案 1 :(得分:1)

我不知道您的要求的详细信息。但是从我的角度来看,我的建议是将流程分为干净的独立和平或责任:

  1. 角色(组织)识别
  2. 依赖角色的文档创建
  3. 文档输出

我认为Print(Options)仅应将文档发送到指定的输出。 Print(Options)必须了解所有文档,但不能汇编它们。现在的主要问题是,例如添加新文档,角色或更多文档数据,因为职责之间没有明确的界限。

考虑一下:在为特定文档创建或配置options参数的那一刻,您已经知道文档的外观。为什么还要麻烦Print(Options)从头开始。一旦知道选项,就可以创建适当的文档。这样,您已经消除了对Options类型的需要,并且更重要的是,对Options对象的状态检查很难维护。

因此,您的流程从对特定于角色的适当方法的调用开始。此方法将创建一个符合此特定角色要求的文档。因此OptionsCreator成为DocumentCreator

创建文档后,您可以将其处理为打印方法。因此Print(Options)成为Print(Document)。重要的是,Print(Document)可以输出文档而无需了解任何有关角色或文档详细信息的信息,而只知道如何以及在何处输出文档。现在,当引入新角色或文档内容时,Print(Document)可以安全地进行修改。您只需将新的工厂方法添加到DocumentCreator即可创建特定于新角色的新文档。

我认为您不能从这里的Builder中受益。