How can I use multiple constructors to remove duplicated code while maintaining readability?

66,315

Solution 1

As an alternative to calling an initialization method from all constructors (which prevents you from using readonly fields) or factory methods (which introduce additional complexity when you have derived classes), you can use a parameter object:

int a, b, c;

public Constructor()
    : this(new ConstructorParameters())
{
}

public Constructor(int x, int y)
    : this(new ConstructorParameters(x, y))
{
}

public Constructor(int x, int y, int z)
{
    a = x;
    b = y;
    c = z;
    //do stuff 
} 

private Constructor(ConstructorParameters parameters)
    : this(parameters.X, parameters.Y, parameters.Z)
{
}

private class ConstructorParameters
{
    public int X;
    public int Y;
    public int Z;

    public ConstructorParameters()
    {
        AnotherThing at = new AnotherThing(param1, param2, param3); 
        Something st = new Something(at, 10, 15) 
        SomethingElse ste = new SomethingElse("some string", "another string"); 

        X = st.CollectionOfStuff.Count; 
        Y = ste.GetValue(); 
        Z = (int)Math.Floor(533 / 39.384); 
    }

    public ConstructorParameters(int x, int y)
    {
        X = x;
        Y = y;
        Z = (int)Math.Floor(533 / 39.384);
    }
}

Solution 2

You can do

Constructor() : this(5, 10, 15)
{
}
Constructor(int x, int y) : this(x, y, 15)
{
}
Constructor(int x, int y, int z)
{
  int a = x;
  int b = y;
  int c = z;
  //do stuff
}

However if you need to do some fancy logic depending on the parameters, I would use a factory pattern:

public class myclass
{
  private myclass(int x, int y, int z)
  {
    int a = x;
    int b = y;
    int c = z;
    //do stuff
  }
  public static myclass Create()
  {
    AnotherThing at = new AnotherThing(param1, param2, param3);
    Something st = new Something(aThing, 10, 15)
    SomethingElse ste = new SomethingElse("some string", "another string");

    int x = thing.CollectionOfStuff.Count;
    int y = ste.GetValue();
    int z = (int)Math.Floor(533 / 39.384);

    return new myclass(x, y ,z);
  }
  public static myclass Create(int x, int y)
  {
    if (x = 1)
      return new myclass(x, y, 2)
    else 
      return new myclass(x, y, 15);
  }
  public static myclass Create(int x, int y, int z)
  {
    //so on and so forth
    return new myclass(x, y, z);
  }
}

Although you don't need a factory pattern, it definitely makes your constructor logic readable.

Share:
66,315

Related videos on Youtube

Cheese
Author by

Cheese

Updated on July 09, 2022

Comments

  • Cheese
    Cheese almost 2 years
    int a, b, c;
    
    Constructor()
    {
        a = 5;
        b = 10;
        c = 15;
        //do stuff
    }
    Constructor(int x, int y)
    {
        a = x;
        b = y;
        c = 15;
        //do stuff
    }
    Constructor(int x, int y, int z)
    {
        a = x;
        b = y;
        c = z;
        //do stuff
    }
    

    To prevent duplication of "stuff" and a few assignments, I tried out something like:

    int a, b, c;
    
    Constructor(): this(5, 10, 15)
    {
    }
    Constructor(int x, int y): this(x, y, 15)
    {
    }
    Constructor(int x, int y, int z)
    {
        a = x;
        b = y;
        c = z;
        //do stuff
    }
    

    This works for what I want to do, but sometimes I need to use some lengthy code to create new objects or do some calculations:

    int a, b, c;
    
    Constructor(): this(new Something(new AnotherThing(param1, param2, param3),
        10, 15).CollectionOfStuff.Count, new SomethingElse("some string", "another
        string").GetValue(), (int)Math.Floor(533 / 39.384))
    {
    }
    Constructor(int x, int y): this(x, y, (int)Math.Floor(533 / 39.384))
    {
    }
    Constructor(int x, int y, int z)
    {
        a = x;
        b = y;
        c = z;
        //do stuff
    }
    

    This code is pretty much the same as before, only the parameters that are being passed aren't very readable. I would prefer doing something like:

    int a, b, c;
    
    Constructor(): this(x, y, z) //compile error, variables do not exist in context
    {
        AnotherThing at = new AnotherThing(param1, param2, param3);
        Something st = new Something(aThing, 10, 15)
        SomethingElse ste = new SomethingElse("some string", "another string");
    
        int x = thing.CollectionOfStuff.Count;
        int y = ste.GetValue();
        int z = (int)Math.Floor(533 / 39.384);
    
        //In Java, I think you can call this(x, y, z) at this point.
        this(x, y, z); //compile error, method name expected
    }
    Constructor(int x, int y): this(x, y, z) //compile error
    {
        int z = (int)Math.Floor(533 / 39.384);
    }
    Constructor(int x, int y, int z)
    {
        a = x;
        b = y;
        c = z;
        //do stuff
    }
    

    Basically I'm building the parameters within the constructor body. Then I'm trying to pass those built parameters to another constructor. I think I remember being able to use the "this" and "super" keywords to call constructors while inside the body of another constructor when coding in Java. It doesn't seem possible in C#.

    Is there a way to do this easily? Did I do something incorrectly? If this is not possible, should I just stick with the unreadable code?

    I guess I could always cut the duplicated code into another method completely outside the constructors. Then each constructor would just do its own thing and call the code shared by the other constructors.

  • Cheese
    Cheese about 12 years
    I think this is a decent solution to what I'm trying to achieve. My project contains several classes that will be used to create collections of anonymous objects. I think a factory pattern could be applied to multiple locations within my code.
  • Cheese
    Cheese about 12 years
    I think I may actually like this route better than the factory methods. I was hesitant to modify my code using factory methods, mainly because I am using inheritance. Being a novice programmer, I was having a hard time trying to visualize all the changes that would need to take place. I have a couple overloaded constructors in my base classes. The derived classes are then adding their own bits to the base constructors. Depending on the derived class, different calculations are needed to find certain parameters. Using a private class to handle complex parameters seems like the simpler approach.
  • Cheese
    Cheese about 12 years
    I decided to go with another solution. After reading through the link you posted, I started reading the "Too Many Parameters" page on the same site. Reading through that made me consider that maybe the constructors were made too complicated by all the parameters I passed to them. I've decided to clip most of the parameters out of the constructors. Setting those fields will be done elsewhere in code. Instead of "how can I do this with constructors?", I think my actual problem was "do I really need to pass that many parameters?". Since the link you supplied led me to my answer...
  • Cheese
    Cheese about 12 years
    and the answer you posted was the one I considered the best solution to my original question, I'm choosing your response as the answer.
  • Makan Tayebi
    Makan Tayebi almost 8 years
    Why isn't it possible in C# to just call a constructor function in another constructor? (If so)
  • Erik Philips
    Erik Philips almost 8 years
    @MakanTayebi sure but sometimes you don't want to construct an object if the parameters are invalid. If that is the case, the majority of the time it is better to use the factory pattern then throw an exception in the constructor.