views:

1742

answers:

14

I'm currently modifying a class that has 9 different constructors. Now overall I believe this class is very poorly designed... so I'm wondering if it is poor design for a class to have so many constructors.

A problem has arisen because I recently added two constructors to this class in an attempt to refactor and redesign a class (SomeManager in the code below) so that it is unit testable and doesn't rely on every one of its methods being static. However, because the other constructors were conveniently hidden out of view about a hundred lines below the start of the class I didn't spot them when I added my constructors.

What is happening now is that code that calls these other constructors depends on the SomeManager class to already be instantiated because it used to be static....the result is a null reference exception.

So my question is how do I fix this issue? By trying to reduce the number of constructors? By making all the existing constructors take an ISomeManager parameter?

Surely a class doesn't need 9 constructors! ...oh and to top it off there are 6000 lines of code in this file!

Here's a censored representation of the constructors I'm talking about above:

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");
}

Update:

Thanks everyone for your responses...they have been excellent!

Just thought I'd give an update on what I've ended up doing.

In order to address the null reference exception issue I've modified the additional constructors to take an ISomeManager.

At the moment my hands are tied when it comes to being allowed to refactor this particular class so I'll be flagging it as one on my todo list of classes to redesign when I have some spare time. At the moment I'm just glad I've been able to refactor the SomeManager class...it was just as huge and horrible as this MyManager class.

When I get around to redesigning MyManager I'll be looking for a way to extract the functionality into two or three different classes...or however many it takes to ensure SRP is followed.

Ultimately, I haven't come to the conclusion that there is a maximum number of constructors for any given class but I believe that in this particular instance I can create two or three classes each with two or three constructors each..

+14  A: 

A class should do one thing and one thing only. If it has so many constructors it seems to be a tell tale sign that it's doing too many things.

Using multiple constructors to force the correct creation of instances of the object in a variety of circumstances but 9 seems like a lot. I would suspect there is an interface in there and a couple of implementations of the interface that could be dragged out. Each of those would likely have from one to a few constructors each relevant to their specialism.

sh1mmer
+2  A: 

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.

Jim Barrows
A: 

Surely a class should have as many constructors as are required by the class... this doesnt mean than bad design can take over.

Class design should be that a constructor creates a valid object after is has finished. If you can do that with 1 param or 10 params then so be it!

simonjpascoe
+1  A: 

Enough to do its task, but remember the Single Responsibility Principle, which states that a class should only have a single responsibility. With that in mind there are probably very few cases where it makes sense to have 9 constructors.

Brian Rasmussen
+1  A: 

Isn't this a line in a Bob Dylan song? :)

cletus
Reminds me of HHGTTG "How many roads must a man walk down?"
Greg Hewgill
That's what I was alluding to.
cletus
+6  A: 

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.

Bhushan
Code 'smell?!' That's a fricken code bullhorn!
Outlaw Programmer
A: 

It seems to me that this class is used to do way, way to much. I think you really should refactor the class and split it into several more specialized classes. Then you can get rid of all these constructors and have a cleaner, more flexible, more maintainable and more readable code.

This was not at direct answer to your question, but i do believe that if it is necessary for a class to have more than 3-4 constructors its a sign that it probably should be refactored into several classes.

Regards.

maskefjes
A: 

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....

krosenvold
+1  A: 

Your problem isn't the number of constructors. Having 9 constructors is more than usual, but I don't think it is necessarily wrong. It's certainly not the source of your problem. The real problem is that the initial design was all static methods. This is really a special case of the classes being too tightly coupled. The now-failing classes are bound to the idea that the functions are static. There isn't much you can do about that from the class in question. If you want to make this class non-static, you'll have to undo all that coupling that was written into the code by others. Modify the class to be non-static and then update all of the callers to instantiate a class first (or get one from a singleton). One way to find all of the callers is to make the functions private and let the compiler tell you.

At 6000 lines, the class is not very cohesive. It's probably trying to do too much. In a perfect world you would refactor the class (and those calling it) into several smaller classes.

Steve Rowe
A: 

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.

Ray Hidayat
+3  A: 

If you arbitrarily limit the number of constructors in a class, you could end up with a constructor that has a massive number of arguments. I would take a class with 100 constructors over a constructor with 100 arguments everyday. When you have a lot of constructors, you can choose to ignore most of them, but you can't ignore method arguments.

Think of the set of constructors in a class as a mathematical function mapping M sets (where each set is a single constructor's argument list) to N instances of the given class. Now say, class Bar can take a Foo in one of its constructors, and class Foo takes a Baz as a constructor argument as we show here:

    Foo --> Bar
    Baz --> Foo

We have the option of adding another constructor to Bar such that:

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

This can be convenient for users of the Bar class, but since we already have a path from Baz to Bar (through Foo), we don't need that additional constructor. Hence, this is where the judgement call resides.

But if we suddenly add a new class called Qux and we find ourselves in need to create an instance of Bar from it: we have to add a constructor somewhere. So it could either be:

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

OR:

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

The later would have a more even distribution of constructors between the classes but whether it is a better solution depends largely on the way in which they are going to be used.

Ryan Delucchi
+5  A: 

As little as possible,
As many as necessary.

Przemek
+1  A: 

I limit my class to only have one real constructor. I define the real constructor as the one that has a body. I then have other constructors that just delegate to the real one depending on their parameters. Basically, I'm chaining my constructors.

Looking at your class, there are four constructors that has a body:

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");
}

The first one is the one that you've added. The second one is similar to the last two but there is a conditional. The last two constructors are very similar, except for the type of parameter.

I would try to find a way to create just one real constructor, making either the 3rd constructor delegate to the 4th or the other way around. I'm not really sure if the first constructor can even fit in as it is doing something quite different than the old constructors.

If you are interested in this approach, try to find a copy of the Refactoring to Patterns book and then go to the Chain Constructors page.

jop
A: 

The awnser is: NONE

Look at the Language Dylan. Its has a other System.

Instat of a constructors you add more values to your slots (members) then in other language. You can add a "init-keyword". Then if you make a instance you can set the slot to the value you want.

Ofcourse you can set 'required-init-keyword:' and there are more options you can use.

It works and it is easy. I dont miss the old system. Writing constructors (and destructors).

(btw. its still a very fast language)


nickik