Static methods considered evil?

1. Oh, cool, static methods!

After learning about static methods for the first time, most people (myself included) become enthusiastic. That’s understandable because static methods have some pretty compelling benefits:

  • They are convenient — You can call static methods whenever you want, without injecting all those pesky dependencies the lead developer keeps telling you about.
public string GetOrderAmount()
{
decimal amount = ShoppingCart.GetAmount();
return $"Your order amount: {amount}";
}
public string GetOrderAmount(ShoppingCart shoppingCart)
{
decimal amount = shoppingCart.GetAmount();
return $"Your order amount: {amount}";
}
public class OrderDetails
{
private ShoppingCart _shoppingCart;
public OrderDetails(ShoppingCart shoppingCart)
{
_shoppingCart = shoppingCart;
}
public string GetOrderAmount()
{
decimal amount = _shoppingCart.GetAmount();
return $"Your order amount: {amount}";
}
}
  • They are faster — Static methods are slightly faster than instance methods because in instance methods, you are also working with an implicit this parameter. Eliminating that parameter gives a slight performance boost in most programming languages.

2. Dependency injection for the win!

After some time, you learn about OOP, SOLID principles and Dependency Injection. You start to understand that static methods introduce some high-level flaws in your code design:

  • Tight coupling — The code that calls static methods is tightly coupled to the called code. No abstraction in-between makes that code hard to test.
  • Obscure dependency graph — The code base that is littered with static method calls is hard to understand because the flow of dependencies is not explicit and you have to read the whole class in order to see the full picture.

3. Meet functional programming

The final epiphany comes when you learn about functional programming. The main idea of functional programming is avoidance of hidden inputs and outputs, such as:

  • A reference to a mutable state,
  • A modification of the mutable state (also known as side effects),
  • Using exceptions to control the program flow.
public class CustomerService
{
private Address _address;
private Customer _customer;

public void Process(string customerName, string addressString)
{
CreateAddress(addressString);
CreateCustomer(customerName);
SaveCustomer();
}

private void CreateAddress(string addressString)
{
_address = new Address(addressString);
}

private void CreateCustomer(string name)
{
_customer = new Customer(name, _address);
}

private void SaveCustomer()
{
var repository = new Repository();
repository.Save(_customer);
}
}
public void Process(string customerName, string addressString)
{
CreateAddress(addressString); // Updates _address
CreateCustomer(customerName); // Needs _address, updates _customer
SaveCustomer(); // Needs _customer
}
public void Process(string customerName, string addressString)
{
CreateCustomer(customerName); // Needs _address
CreateAddress(addressString);
SaveCustomer();
}
public class CustomerService
{
public void Process(string customerName, string addressString)
{
Address address = CreateAddress(addressString);
Customer customer = CreateCustomer(customerName, address);
SaveCustomer(customer);
}

private Address CreateAddress(string addressString)
{
return new Address(addressString);
}

private Customer CreateCustomer(string name, Address address)
{
return new Customer(name, address);
}

private void SaveCustomer(Customer customer)
{
var repository = new Repository();
repository.Save(customer);
}
}
public class CustomerService
{
public void Process(string customerName, string addressString)
{
Address address = CreateAddress(addressString);
Customer customer = CreateCustomer(customerName, address);
SaveCustomer(customer);
}
// the method is static now
private static Address CreateAddress(string addressString)
{
return new Address(addressString);
}

// the method is static now
private static Customer CreateCustomer(string name, Address address)
{
return new Customer(name, address);
}
// the method is static now
private static void SaveCustomer(Customer customer)
{
var repository = new Repository();
repository.Save(customer);
}
}

4. Conclusion

I’ve come full circle on the use of static methods throughout my programming career. Just a few years ago, I tried to always inject all dependencies explicitly. Nowadays, I do so only for stateful (or out-of-process) dependencies. If a dependency is stateless, I make it static and call it directly.

Subscribe

Subscribe to read more articles like this: https://enterprisecraftsmanship.com/subscribe

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store