请原谅我对设计模式的了解。
有时候方法有很多参数,引入 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 };
}
}
但是我怕上面的例子是反模式的。
我能想到的另一件事是构建器模式。
构建器模式是否是参数对象的最佳解决方案?
答案 0 :(得分:1)
我通常这样使用smth:
带有-的常量类(在此示例中,我将第一个参数用于字符串以进行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) },
}
像这样处理您的选项(伪代码,假设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)
我不知道您的要求的详细信息。但是从我的角度来看,我的建议是将流程分为干净的独立和平或责任:
我认为Print(Options)
仅应将文档发送到指定的输出。 Print(Options)
必须了解所有文档,但不能汇编它们。现在的主要问题是,例如添加新文档,角色或更多文档数据,因为职责之间没有明确的界限。
考虑一下:在为特定文档创建或配置options参数的那一刻,您已经知道文档的外观。为什么还要麻烦Print(Options)
从头开始。一旦知道选项,就可以创建适当的文档。这样,您已经消除了对Options
类型的需要,并且更重要的是,对Options
对象的状态检查很难维护。
因此,您的流程从对特定于角色的适当方法的调用开始。此方法将创建一个符合此特定角色要求的文档。因此OptionsCreator
成为DocumentCreator
。
创建文档后,您可以将其处理为打印方法。因此Print(Options)
成为Print(Document)
。重要的是,Print(Document)
可以输出文档而无需了解任何有关角色或文档详细信息的信息,而只知道如何以及在何处输出文档。现在,当引入新角色或文档内容时,Print(Document)
可以安全地进行修改。您只需将新的工厂方法添加到DocumentCreator
即可创建特定于新角色的新文档。
我认为您不能从这里的Builder中受益。