English 中文(简体)
一个类应该有多少个构造方法?
原标题:
  • 时间:2009-01-29 06:29:26
  •  标签:

我目前正在修改一个有九种不同构造函数的类。现在总体来说,我认为这个类的设计非常糟糕......所以我想知道,一个类有这么多构造函数是不是一个不好的设计。

因为我最近尝试重构和重新设计一个类(下面代码中的 "SomeManager")以使它能够进行单元测试并且不依赖于其每个方法都是静态的,所以我添加了两个构造函数,但是一个问题已经出现了。但是,由于其他构造函数方便地隐藏在类的开头以下大约一百行处,当我添加我的构造函数时我没有注意到它们。

现在发生的情况是调用这些其他构造函数的代码取决于SomeManager类已经被实例化,因为它曾经是静态的... 结果是一个空引用异常。

那么我的问题是如何解决这个问题?通过尝试减少构造函数的数量吗?通过使所有现有的构造函数都接受ISomeManager参数吗?

肯定一个类不需要9个构造器!…哦,最糟糕的是这个文件有6000行代码!

这是我上面所谈论的构造函数的被屏蔽表示:

public MyManager()
    : this(new SomeManager()){} //this one I added

public MyManager(ISomeManager someManager) //this one I added
{
    this.someManager = someManager;
}

public MyManager(int id)
    : this(GetSomeClass(id)) {}

public MyManager(SomeClass someClass)
    : this(someClass, DateTime.Now){}

public MyManager(SomeClass someClass, DateTime someDate)
{
    if (someClass != null)
       myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(SomeOtherClass someOtherClass)
    : this(someOtherClass, DateTime.Now){}

public MyManager(SomeOtherClass someOtherClass, DateTime someDate)
{
    myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(YetAnotherClass yetAnotherClass)
    : this(yetAnotherClass, DateTime.Now){}

public MyManager(YetAnotherClass yetAnotherClass, DateTime someDate)
{
    myHelper = new MyHelper(yetAnotherClass, someDate, "some param");
}

更新:

谢谢大家的回应,非常棒!

我想向你们告知一下我最终做了什么。

为了解决空引用异常问题,我已经修改了额外的构造函数,将其改为接收一个 ISomeManager。

目前,当涉及到被允许重构这个特定的类时,我的手是被绑住的,因此我会将它标记为待办清单中需要重新设计的类之一,等我有空闲时间时。 目前我很高兴我能够重构SomeManager类...它和这个MyManager类一样庞大可怕。

当我开始重新设计MyManager时,我会寻找一种方法将功能提取到两个或三个不同的类中...或者需要多少个类来确保SRP被遵循。

最终,我还没有得出任何给定类的构造函数数量上限的结论,但我相信在这种特定情况下,我可以创建两个或三个类,每个类都有两个或三个构造函数。

最佳回答

一个类应该只做一件事。如果它有太多的构造函数,似乎是一个显而易见的迹象表明它正在做太多的事情。

使用多个构造函数来强制正确创建对象实例以适应各种情况,但似乎有9个构造函数太多了。我会怀疑其中有一个接口,以及几个可以拖出来的接口实现。每个实现都可能有从一个到几个构造函数,每个构造函数都与它们的专业领域相关。

问题回答

As little as possible,
As many as necessary.

9 constructors and 6000 lines in class is a sign of code smell. You should re-factor that class. If the class is having lot of responsibilities and then you should separate them out. If the responsibilities are similar but little deviation then you should look to implement inheritance buy creating a interface and different implementations.

如果您随意限制类中的构造函数数量,可能会得到一个具有大量参数的构造函数。我每天都会选择拥有100个构造函数的类而不是100个参数的构造函数。当您有大量构造函数时,可以选择忽略它们中的大部分,但是您不能忽略方法参数。

将类中的构造函数集合视为数学函数,将M个集合(其中每个集合是一个单独的构造函数参数列表)映射到给定类的N个实例。现在假设类 Bar 可以使用其中一个构造函数接受 Foo ,而类 Foo 使用 Baz 作为构造函数参数,如下所示:

    Foo --> Bar
    Baz --> Foo

我们有添加另一个构造函数到Bar的选项,如下:

    Foo --> Bar
    Baz --> Bar
    Baz --> Foo

这对于Bar类的用户来说可能很方便,但是既然我们已经有了从Baz到Bar的路径(通过Foo),我们就不需要那个额外的构造函数。因此,这就是判断的关键所在。

但是,如果我们突然添加一个名为Qux的新类,并且我们发现需要从中创建Bar的一个实例:我们必须在某处添加一个构造函数。因此,它可以是:

    Foo --> Bar
    Baz --> Bar
    Qux --> Bar
    Baz --> Foo

或者

    Foo --> Bar
    Baz --> Bar
    Baz --> Foo
    Qux --> Foo

后者将在类之间拥有更均衡的构造函数分布,但是否是更好的解决方案主要取决于它们将如何使用。

答案:1(关于注射剂)。

这是一篇关于该主题的精彩文章:依赖注入反模式:多个构造函数

总结一下,你的类的构造函数应该用于注入依赖项,而且你的类应该公开其依赖关系。依赖关系是你的类需要的东西,而不是它想要的或可以不用的东西,它是必需的。

所以,对我来说,具有可选的构造函数参数或重载的构造函数毫无意义。你唯一的公共构造函数应该定义你的类的依赖关系,这是你的类所提供的合同,它说:“如果你给我一个IDigitalCamera,一个ISomethingWorthPhotographing和一个IBananaForScale,我会给你想象中最好的IPhotographWithScale。但是,如果你少了任何一样东西,那就由你自己来承担风险了。”

这里有一篇文章,由Mark Seemann撰写,探讨了拥有一个规范构造函数的一些更好的原因:Statement Your Dependency Intent

It s not just this class you have to worry about re-factoring. It s all the other classes as well. And this is probably just one thread in the tangled skein that is your code base. You have my sympathy... I m in the same boat. Boss wants everything unit tested, doesn t want to rewrite code so we can unit test. End up doing some ugly hacks to make it work. You re going to have to re-write everything that is using the static class to no longer use it, and probably pass it around a lot more... or you can wrap it in a static proxy that accessses a singleton. That way you an at least mock the singleton out, and test that way.

你的问题不是构造函数的数量。有9个构造函数比平常多,但我认为这并不一定是错的。这肯定不是你问题的根源。真正的问题是最初的设计全部都是静态方法。这实际上是因为类太过紧密地耦合了。现在会出现故障的类与函数是静态绑定的。从相关类无法解决这个问题。如果你想使这个类非静态,你必须撤销其他人在代码中写入的所有耦合。修改类使之非静态,然后更新所有调用程序,以实例化一个类(或从单例中获取一个)。找到所有的调用方法的一种方法是将这些函数设为私有,然后让编译器告诉你。

在6000行处,类的内聚性不是很好。它可能试图做太多事情。在完美的世界里,您应该将类(以及调用它的类)重构为几个更小的类。

足以完成它的任务,但请记住单一职责原则,即类应该只有一个职责。有了这个原则,可能很少有情况需要有9个构造函数。

我将我的类限制为只有一个真正的构造函数。我将真正的构造函数定义为具有主体的构造函数。然后,我有其他构造函数,根据其参数委托给真正的构造函数。基本上,我在链接我的构造函数。

看看你的类,有四个带有实现的构造函数:

public MyManager(ISomeManager someManager) //this one I added
{
    this.someManager = someManager;
}

public MyManager(SomeClass someClass, DateTime someDate)
{
    if (someClass != null)
       myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(SomeOtherClass someOtherClass, DateTime someDate)
{
    myHelper = new MyHelper(someOtherClass, someDate, "some param");
}

public MyManager(YetAnotherClass yetAnotherClass, DateTime someDate)
{
    myHelper = new MyHelper(yetAnotherClass, someDate, "some param");
}

第一个是你添加的那个。第二个与后两个相似,但存在条件。最后两个构造函数非常相似,除了参数类型。

我将尝试寻找一种方法来创建一个真正的构造函数,使第三个构造函数委托给第四个构造函数或者反过来。我并不确定第一个构造函数是否适合,因为它所做的事情与旧构造函数有很大的不同。

如果你对这种方法感兴趣,可以尝试找一本《重构到模式》的书,然后进入Chain Constructors页面。

一个类必须拥有所需数量的构造函数...这并不意味着不良设计可以取代它。 (Simplified Chinese)

类的设计应该是:构造函数在完成后应该创建一个有效的对象。如果你可以用1个参数或10个参数实现这一点,那就这样做!

我认为这个类的用途太多了。我认为你应该重构这个类,并将其拆分成更多专业化的类。这样你可以摆脱所有这些构造函数,让代码更加干净、灵活、易于维护和易于阅读。

这不是对你的问题的直接回答,但我相信,如果一个类需要有超过3-4个构造函数,那么这可能意味着它应该被重构为几个类。

Regards.

The only "legit" case I can see from you code is if half of them are using an obsolete type that you are working to remove from the code. When I work like this I frequently have double sets of constructors, where half of them are marked @Deprecated or @Obsolete. But your code seems to be way beyond that stage....

I generally have one, which may have some default parameters. The constructor will only do the minimum setup of the object so it s valid by the time it s been created. If I need more, I ll create static factory methods. Kind of like this:

class Example {
public:
  static FromName(String newname) { 
    Example* result = new Example();
    result.name_ = newname;
    return result;
  }
  static NewStarter() { return new Example(); }

private:
  Example();
}

Okay that s not actually a very good example, I ll see if I can think of a better one and edit it in.

答案是:没有

看看Dylan语言。它有另一个系统。

与其他语言不同的是,您可以向插槽(成员)添加更多的值,而无需使用构造函数。您可以添加一个“init-keyword”。然后,如果您创建一个实例,您可以将插槽设置为您想要的值。

当然你可以设置必需的初始化关键字,并且还有更多选项可供使用。

它运行良好且容易操作。我不怀念旧系统。编写构造函数(和析构函数)。

顺便说一句,它仍然是一种非常快的语言。


我认为拥有多个构造函数的类就会有多个职责。然而,被证实相反会很好。

A constructor should have only those arguments which are mandatory for creating the instance of that class. All other instance variables should have corresponding getter and setter methods. This will make your code flexible if you plan to add new instance variables in the future.

In fact following OO principle of -

  1. For each class design aim for low coupling and high cohesion
  2. Classes should be open for extension but closed for modification.

    you should have a design like -

    import static org.apache.commons.lang3.Validate.*; public class Employee { private String name; private Employee() {}

    public String getName() { return name; }

    public static class EmployeeBuilder { private final Employee employee;

    public EmployeeBuilder()
    {
        employee = new Employee();
    }
    
    public EmployeeBuilder setName(String name)
    {
        employee.name = name;
        return this;
    }
    
    public Employee build()
    {
        validateFields();
        return employee;
    }
    
    private void validateFields()
    {
        notNull(employee.name, "Employee Name cannot be Empty");
    }
    

    } }





相关问题
热门标签