public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public static Person CreateNew()
{
Person temp = new Person();
temp.Id = -1;
temp.FirstName = String.Empty;
temp.LastName = String.Empty;
return temp;
}
public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
public class PersonApp
{
public void DoSomething()
{
// static method
Person temp = Person.CreateNew();
temp.FirstName = "Ben";
temp.LastName = "Day";
// instance method
Console.WriteLine(temp.ToString());
}
}
On the Person class, we've encapsulated some Factory Pattern creation logic inside of a static method called CreateNew() and there is also an instance method implementation of ToString(). At this point, the static method seems harmless but let's assume that, like most software, the application grows and changes over time. What's the harm in that static method? The answer: maintenance and testability.
Why are static methods bad for maintenance? Well, static methods cannot be overridden or extended through inheritance. Pretend that we've been handed a new requirement and now we need a class called Employee that extends from Person and introduces two new properties: Boss and Department. As part of the requirements for the Employee class, the ToString() and CreateNew() methods need to be updated.
public class Employee : Person
{
public string Boss { get; set; }
public string Department { get; set; }
public override string ToString()
{
return String.Format("{0} {1} -- {2}", FirstName, LastName, Department);
}
}
Providing this new implementation of ToString() on Employee is easy. All you have to do is override ToString() again and it replaces the implementation that's on Person. So, we get to keep all the properties from Person and add on the additional Employee fields and then change the ToString() implementation. Very easy, very basic object-oriented programming.
The implementation of CreateNew() for Employee isn't going to be as smooth. What we really want to be able to do is to take the implementation of CreateNew() from the base class (Person) and extend it so that it initializes the Boss and Department properties. The problem is that we get a compiler error if we mark a static method with override, virtual, or abstract.
This pretty much leaves us no choice. We have to duplicate the code from Person.CreateNew() in Employee.CreateNew(). That's definitely a worst practice.
public class Employee : Person
{
public string Boss { get; set; }
public string Department { get; set; }
public override string ToString()
{
return String.Format("{0} {1} -- {2}", FirstName, LastName, Department);
}
public static Employee CreateNew()
{
Employee temp = new Employee();
temp.Id = -1;
temp.FirstName=String.Empty;
temp.LastName = String.Empty;
temp.Boss = String.Empty;
temp.Department = String.Empty;
return temp;
}
}
The other reason that I try to avoid static methods is because they're bad for testability and impossible to use with interface-driven programming. If you're using and writing unit tests, you'll quickly realize how much simpler and cleaner that your unit tests can be if you code against interfaces. If your application's code depends on interfaces rather than concrete objects, your application is going to be much more flexible. This interface flexibility will allow you to use "mock" or otherwise faked out implementations of your objects in your unit tests. Mock objects help to keep your tests focused on what you're trying to test by freeing you from having to set up and manage huge chains of object dependencies.
If you want to create an IPerson interface to represent a Person, there's no way to get that CreateNew() method on there because you can't have static methods on an interface. That's a big roadblock.
Why you might be tempted to write a lot of static methods
I tend to find developers getting in to the static method trap when they're trying to write a stateless application like a Web application or a WCF service. In a stateless application, there's no in-memory state data hanging around between calls from the user. For example, in a WCF service application, a WCF client makes a call to a service method, the service processes the request, returns a result to the client, and is disposed. The lifetime of the object that services that WCF request is very short.
Summary
If you find yourself writing a lot of static methods, you're probably doing something wrong. Static methods work best as utility methods (think System.Math) or other very tightly scoped methods. If you can imagine ever needing to use object inheritance or needing to use polymorphism for your method, you should definitely skip the static and make it an instance method.
This was first published in February 2010