I tend to declare as static all the methods in a class when that class doesn't require to keep track of internal states. For example, if I need to transform A into B and don't rely on some internal state C that may vary, I create a static transform. If there is an internal state C that I want to be able to adjust, then I add a constructor to set C and don't use a static transform.
I read various recommendations (including on StackOverflow) NOT to overuse static methods but I still fail to understand what it wrong with the rule of thumb above.
Is that a reasonable approach or not?
I'd consider it a design smell. If you find yourself using mostly static methods, you probably don't have a very good OO design. It's not necessarily bad, but as with all smells it would make me stop and re-evaluate. It hints that you might be able make a better OO design, or that maybe you should go the other direction and avoid OO entirely for this problem.
Static classes are fine as long as they're used in the right places.
Namely: Methods that are 'leaf' methods (they do not modify state, they merely transform the input somehow). Good examples of this are things like Path.Combine. These sorts of things are useful and make for terser syntax.
The problems I have with statics are numerous:
Firstly, if you have static classes, dependencies are hidden. Consider the following:
Looking at TextureManager, you cannot tell what initialisation steps must be carried out by looking at a constructor. You must delve into the class to find its dependencies and initialise things in the correct order. In this case, it needs the ResourceLoader to be initialised before running. Now scale up this dependency nightmare and you can probably guess what will happen. Imagine trying to maintain code where there is no explicit order of initialisation. Contrast this with dependency injection with instances -- in that case the code won't even compile if the dependencies are not fulfilled!
Furthermore, if you use statics that modify state, it's like a house of cards. You never know who has access to what, and the design tends to resemble a spaghetti monster.
Finally, and just as importantly, using statics ties a program to a specific implementation. Static code is the antithesis of designing for testability. Testing code that is riddled with statics is a nightmare. A static call can never be swapped for a test double (unless you use testing frameworks specifically designed to mock out static types), so a static system causes everything that uses it to be an instant integration test.
In short, statics are fine for some things and for small tools or throwaway code I wouldn't discourage their use. However, beyond that, they are a bloody nightmare for maintainability, good design and ease of testing.
Here's a good article on the problems: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/
There are two kinds of common static methods:
There are a few common uses of "unsafe" statics -- for example, in the Singleton pattern -- but be aware that despite any pretty names you call them, you're just mutating global variables. Think carefully before using unsafe statics.
An object without any internal state is a suspicious thing.
Normally, objects encapsulate state and behavior. An object that only encapsulates behavior is odd. Sometimes it's an example of Lightweight or Flyweight.
Other times, it's procedural design done in an object language.
If you know you will never need to use the internal state of C, it's fine. Should that ever change in the future, though, you'd need to make the method non-static. If it's non-static to begin with, you can just ignore the internal state if you don't need it.
The other option is to add them as non-static methods on the originating object:
i.e., changing:
into
however in many situations this isn't possible (e.g., regular class code generation from XSD/WSDL/etc), or it will make the class very long, and transformation methods can often be a real pain for complex objects and you just want them in their own separate class. So yeah, I have static methods in utility classes.