Inappropriate Static — Code Smells Catalog Skip to content

Inappropriate Static

Also known as: Static Cling

Object Oriented Abusers Interfaces Code SmellDesign Smell Between Class

Impossible to override. Painful to mock. Silently coupling everything that calls them. Static methods are convenient right up until the behavior needs to vary: then they're a dead end.

2 min read 1 source

Overview

The rule of thumb given by uncle Robert is that when in doubt, one should prefer non-static methods to static methods. The best way to check whether a method should be static would be to think if the method should behave polymorphically. An excellent example of a static method given by Martin is math.max(double a, double b) - it is hard to think of polymorphic behavior for a max function. On the other hand, HourlyPayCalculator.calculate_pay(employee, overtime_rate) is dubious, as there could be different algorithms to calculate the payment, and thus it should be a nonstatic method of class Employee. However, one should be aware and take caution of the Speculative Generality code smell. At the very present moment, when there are no different algorithms yet requested or planned, this is a stateless operation, which is acceptable for static methods. Steve Smiths addresses that statics should be reserved for behavior that will never change, besides the previously mentioned stateless operations, giving global constants as examples [1].

Static Cling is a code smell based on the border of the test code and the source code, although, following the Fail-Fast principle, the issue starts in the developing parts of the codebase.

Whenever a static function is called, in most languages, it is, to say the least, not trivial to test or mock the method in which it occurs. There are 3rd party mocking frameworks, but that is more of a workaround for bad design. Developers should look out for these dependencies because they are effortlessly introduced into the code in styles other than Test-Driven Development.

Problems

🧪
Hard to Test

Using static functions and variables makes the code harder to test; requires mocking.

🔗
Coupling
Single Responsibility Principle Violation

Example

1class FooUtils:
2    @staticmethod
3    def wrap_tag_premium(foo: Foo):
4        return f"[TAG]: {foo.action()}"
5
6    @staticmethod
7    def wrap_tag_special(foo: Foo):
8        return f"[TAG]: {foo.action()}"
PYTHON

Refactoring

  • Inject Dependencies

Sources

Browse All 56 Smells