33

I am implementing a DelegateCommand, and when I was about to implement the constructor(s), I came up with the following two design choices:

1: Having multiple overloaded constructors

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2: Having only one constructor with an optional parameter

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

I don't know which one to use because I don't know what possible advantages / disadvantages come with either of the two proposed ways. Both can be called like this:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

Can someone please point me in the right direction and give feedback?

3 Answers3

25

I prefer multiple constructors over default values and personally I don't like your two constructor example, it should be implemented differently.

The reason for using multiple constructors is that the main one can just check if all parameters are not null and whether they are valid whereas other constructors can provide default values for the main one.

In your examples however, there is no difference between them because even the secondary constructor passes a null as a default value and the primary constructor must know the default value too. I think it shouldn't.

This means that it would be cleaner and better separated if implemented this way:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

notice the _ => true passed to the primary constructor that is now also checking all parameters for null and doesn't care about any defaults.


The most important point however is extensibility. Multiple constructors are safer when there is a possibility that you will extend your code in the future. If you add more required parameters, and the optional ones must come at the end, you'll break all your current implementations. You can make the old constructor [Obsolete] and inform the users that it's going to be removed giving them time to migrate to the new implementation without instantly breaking their code.


On the other hand making too many parameters optional would be confusing too because if some of them are required in one scenario and optional in another one you would need to study the documentation instead of just picking the right constructor by simply looking at its parameters.

t3chb0t
  • 2,602
17

Considering the only thing you're doing in the constructor is simple assignment, the single constructor solution may be a better choice in your case. The other constructor provides no extra functionality and it's clear from the design of the constructor with two parameters that the second argument does not need to be supplied.

Multiple constructors do make sense when you're constructing an object from different types. In that case, the constructors are a substitution of an external factory because they process the input parameters and format them to a correct internal property representation of the class which is being constructed. That does not however happen in your case hence why a single constructor should be more than enough.

Andy
  • 10,400
2

I think there's two different cases for which each approach can shine.

Firstly, when we have simple constructors (which is usually the case, in my experience), I would consider optional arguments to shine.

  1. They minimize code that has to be written (and thus also that has to be read).
  2. You can ensure that documentation is in one place and isn't repeated (which is bad because it opens up an extra area that could get outdated).
  3. If you have many optional arguments, you can avoid having a very confusing set of combinations of constructors. Heck, even with just 2 optional arguments (unrelated to each other), if you wanted separate, overloaded constructors, you'd have to have 4 constructors (version without any, version with each, and version with both). This obviously doesn't scale well.

Buuuut, there are cases where optional arguments in constructors just make things clearly more confusing. The obvious example is when these optional arguments are exclusive (ie, can't be used together). Having overloaded constructors that only permit the actually valid combinations of arguments would ensure compile time enforcement of this constraint. That said, you should also avoid even encountering this case (eg, with multiple classes inheriting from a base, where each class does the exclusive behavior).

Kat
  • 330
  • 2
  • 10