Is using a lot of static methods a bad thing?

2019-01-05 09:36发布

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?

15条回答
我只想做你的唯一
2楼-- · 2019-01-05 09:47

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.

查看更多
我想做一个坏孩纸
3楼-- · 2019-01-05 09:52

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:

public static class ResourceLoader
{
    public static void Init(string _rootPath) { ... etc. }
    public static void GetResource(string _resourceName)  { ... etc. }
    public static void Quit() { ... etc. }
}

public static class TextureManager
{
    private static Dictionary<string, Texture> m_textures;

    public static Init(IEnumerable<GraphicsFormat> _formats) 
    {
        m_textures = new Dictionary<string, Texture>();

        foreach(var graphicsFormat in _formats)
        {
              // do something to create loading classes for all 
              // supported formats or some other contrived example!
        }
    }

    public static Texture GetTexture(string _path) 
    {
        if(m_textures.ContainsKey(_path))
            return m_textures[_path];

        // How do we know that ResourceLoader is valid at this point?
        var texture = ResourceLoader.LoadResource(_path);
        m_textures.Add(_path, texture);
        return texture; 
    }

    public static Quit() { ... cleanup code }       
}

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/

查看更多
来,给爷笑一个
4楼-- · 2019-01-05 09:55

There are two kinds of common static methods:

  • A "safe" static method will always give the same output for the same inputs. It modifies no globals and doesn't call any "unsafe" static methods of any class. Essentially, you are using a limited sort of functional programming -- don't be afraid of these, they're fine.
  • An "unsafe" static method mutates global state, or proxies to a global object, or some other non-testable behavior. These are throwbacks to procedural programming and should be refactored if at all possible.

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.

查看更多
ら.Afraid
5楼-- · 2019-01-05 09:55

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.

查看更多
淡お忘
6楼-- · 2019-01-05 09:59

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.

查看更多
\"骚年 ilove
7楼-- · 2019-01-05 10:00

The other option is to add them as non-static methods on the originating object:

i.e., changing:

public class BarUtil {
    public static Foo transform(Bar toFoo) { ... }
}

into

public class Bar {
    ...
    public Foo transform() { ...}
}

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.

查看更多
登录 后发表回答