Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ssis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parameters 有多少构造函数参数太多?_Parameters_Refactoring_Constructor_Coding Style - Fatal编程技术网

Parameters 有多少构造函数参数太多?

Parameters 有多少构造函数参数太多?,parameters,refactoring,constructor,coding-style,Parameters,Refactoring,Constructor,Coding Style,假设您有一个名为Customer的类,它包含以下字段: 用户名 电子邮件 名字 姓 还可以说,根据您的业务逻辑,所有客户对象都必须定义这四个属性 现在,我们可以很容易地通过强制构造函数指定这些属性中的每一个来做到这一点。但是,当您被迫向Customer对象添加更多必填字段时,很容易看出这是如何失控的 我见过一些类将20多个参数引入到它们的构造函数中,使用它们只是一种痛苦。但是,或者,如果不需要这些字段,则可能会遇到未定义信息的风险,或者更糟糕的是,如果依赖调用代码来指定这些属性,则会出现对象

假设您有一个名为Customer的类,它包含以下字段:

  • 用户名
  • 电子邮件
  • 名字
还可以说,根据您的业务逻辑,所有客户对象都必须定义这四个属性

现在,我们可以很容易地通过强制构造函数指定这些属性中的每一个来做到这一点。但是,当您被迫向Customer对象添加更多必填字段时,很容易看出这是如何失控的

我见过一些类将20多个参数引入到它们的构造函数中,使用它们只是一种痛苦。但是,或者,如果不需要这些字段,则可能会遇到未定义信息的风险,或者更糟糕的是,如果依赖调用代码来指定这些属性,则会出现对象引用错误


是否有其他选择,或者您只需决定X个构造函数参数是否太多,您无法接受?

除非它超过1个参数,我总是使用数组或对象作为构造函数参数,并依靠错误检查来确保所需的参数在那里。

如果您有很多参数,那么只需将它们打包到structs/POD类中,最好声明为您正在构造的类的内部类。这样,在使调用构造函数的代码具有合理可读性的同时,您仍然可以需要这些字段。

只需使用默认参数即可。在支持默认方法参数的语言中(例如PHP),可以在方法签名中执行此操作:

公共函数doSomethingWith($this=val1,$this=val2,$this=val3)

还有其他方法可以创建默认值,例如在支持方法重载的语言中

当然,您也可以在声明字段时设置默认值,如果您认为这样做合适的话


实际上,这取决于您是否适合设置这些默认值,或者您的对象是否应该始终在构造时进行指定。这真的是一个只有你才能做出的决定。

风格非常重要,在我看来,如果有一个构造函数有20多个参数,那么设计应该改变。提供合理的默认值。

我认为这取决于具体情况。对于您的示例,一个客户类,我不会冒险在需要时不定义该数据。另一方面,传递一个结构会清除参数列表,但是在结构中仍然有很多东西需要定义

我认为最简单的方法是为每个值找到一个可接受的默认值。在这种情况下,每个字段看起来都需要构造,因此可能会重载函数调用,以便在调用中未定义某些内容时,将其设置为默认值

然后,为每个属性生成getter和setter函数,以便可以更改默认值

Java实现:

public static void setEmail(String newEmail){
    this.email = newEmail;
}

public static String getEmail(){
    return this.email;
}

这也是保持全局变量安全的好方法。

史蒂夫·麦康奈尔(Steve Mcconnell)用完整的代码写道,人们在一次记住7件事情时会遇到困难,所以我会尽量保持在这个数字之下

我同意Boojiboy提到的7项限制。除此之外,还值得研究匿名(或专用)类型、IDictionary或通过主键间接指向另一个数据源。

我认为“纯OOP”的答案是,如果在某些成员未初始化时类上的操作无效,那么这些成员必须由构造函数设置。总是有使用默认值的情况,但我假设我们没有考虑这种情况。当API被修复时,这是一个好方法,因为在API公开后更改单个允许的构造函数对您和代码的所有用户来说都是一场噩梦

在C#中,我对设计指南的理解是,这不一定是处理这种情况的唯一方法。特别是对于WPF对象,您会发现.NET类倾向于使用无参数构造函数,如果在调用方法之前数据尚未初始化到所需的状态,则会抛出异常。这可能主要是针对基于组件的设计;我想不出一个具体的.NET类以这种方式运行的例子。在您的情况下,这肯定会增加测试的负担,以确保类永远不会保存到数据存储中,除非属性已经过验证。老实说,正因为如此,如果您的API是一成不变的,或者不是公开的,我更喜欢“构造函数设置所需属性”的方法


我可以肯定的一点是,可能有无数种方法可以解决这个问题,每种方法都引入了自己的一组问题。最好的办法是学习尽可能多的模式,并为工作选择最好的模式。(这不是在逃避回答吗?

我认为你的问题更多的是关于类的设计,而不是构造函数中的参数数量。如果需要20个数据(参数)来成功初始化一个对象,我可能会考虑分解类。

< P>两种设计方法考虑

模式

模式

这两者在意图上都是相似的,我们慢慢地建立一个中间对象,然后在一个步骤中创建我们的目标对象

fluent接口的一个实例是:

公共类CustomerBuilder{
串姓;
字符串名;
字符串ssn;
公共静态CustomerBuilder客户(){
返回新的CustomerBuilder();
}
具有URNAME(字符串姓氏)的公共CustomerBuilder{
this.姓氏=姓氏;
归还这个;
}
名为(字符串fi)的公共CustomerBuilder
 public class NutritionFacts {  
   private final int servingSize;  
   private final int servings;  
   private final int calories;  
   private final int fat;  
   private final int sodium;  
   private final int carbohydrate;  

   public static class Builder {  
     // required parameters  
     private final int servingSize;  
     private final int servings;  

     // optional parameters  
     private int calories         = 0;  
     private int fat              = 0;  
     private int carbohydrate     = 0;  
     private int sodium           = 0;  

     public Builder(int servingSize, int servings) {  
      this.servingSize = servingSize;  
       this.servings = servings;  
    }  

     public Builder calories(int val)  
       { calories = val;       return this; }  
     public Builder fat(int val)  
       { fat = val;            return this; }  
     public Builder carbohydrate(int val)  
       { carbohydrate = val;   return this; }  
     public Builder sodium(int val)  
       { sodium = val;         return this; }  

     public NutritionFacts build() {  
       return new NutritionFacts(this);  
     }  
   }  

   private NutritionFacts(Builder builder) {  
     servingSize       = builder.servingSize;  
     servings          = builder.servings;  
     calories          = builder.calories;  
     fat               = builder.fat;  
     soduim            = builder.sodium;  
     carbohydrate      = builder.carbohydrate;  
   }  
}  
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
      calories(100).sodium(35).carbohydrate(27).build();
setOuterBounds(x, y, width, height);
setInnerBounds(x + 2, y + 2, width - 4, height - 4);
setOuterBounds(bounds);
setInnerBounds(bounds.expand(-2));
ContactInfo cinfos = new ContactInfo[] {
    new ContactInfo("home", "+123456789", "123 ABC Avenue"),
    new ContactInfo("biz", "+987654321", "789 ZYX Avenue")
};

Customer c = new Customer("john", "doe", cinfos);
class Customer {
    private string name;
    private int age;
    private string email;

    Customer(string name, int age, string email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

class John : Customer {
    John() : base("John", 20, "John@email.com") { 

    }
}
class Customer {
    protected abstract string name { get; }
    protected abstract int age { get; }
    protected abstract string email { get; }
}

class John : Customer {
    protected override string name => "John";
    protected override int age => 20;
    protected override string email=> "John@email.com";
}