Without Precedence

Refactoring: Named boolean arguments

by Morten Christiansen on 22-01-2012 at 12:29 | comments [0] | posted in Best Practices, Refactoring

This is another article in my series on refactoring code to make it more readable and maintainable

Boolean arguments have a nasty tendency of making method calls very hard to read, especially when several of them are required. For example, consider this piece of code I just ran across:

  1. new SchemaExport(Configuration).Execute(true, true, false, true, session.Connection, Console.Out);

This might not by a typical example, but it clearly illustrates the problem. Just looking at it, you don't really know much about what this line of code does. Of course, your IDE can probably help you figure it out quickly enough, but several such lines can it makes it very hard to quickly figure out a section of code. I've run across variations of this issue countless times and have come to use one of two approaches to avoid this, depending on the circumstances.

The first is to use named variables, a feature of C#. Alternatively, you can declare a variable and supply it to the method. This makes for much clearer code:

  1. // named arguments
  2. var users = service.GetUsers(includeTemporary: true, loadDocuments: true);
  3.  
  4. // named variables as arguments
  5. var includeTemporary = true;
  6. var loadDocuments = true;
  7. var users = service.GetUsers(includeTemporary, loadDocuments);

Most of the time, I find this to be a suitable solution. Sometimes, however, the boolean argument has such an impact on the implementation of the method that we're better served splitting it in two. This will keep each of the methods focused on a single responsibility making it easier to change one part without affecting the other. In the example below, the method is split into two because the notions of a document and a draft are, while related, not treated the same at all. Therefore, a more appropriate API would have methods for getting either type separately.

  1. // wrong way (because drafts and normal documents are never treated as the same thing)
  2. var documents = service.GetDocuments(includeDrafts: false);
  3. var documentsAndDrafts = service.GetDocuments(includeDrafts: true);
  4.  
  5. // better way
  6. var documents = service.GetDocuments();
  7. var drafts = service.GetDrafts();

If needed, the implementations of the two methods can rely on secondary methods for any shared logic.

Comments