ChatGPT解决这个技术问题 Extra ChatGPT

Is using a lot of static methods a bad thing?

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?


K
Kim Desrosiers

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.


This was exactly the problem I had to solve - the use, or rather misuse, of Singleton objects.
Thank you for that most excellent answer. My question is, if the singletons are passed in as parameters to the static methods, does that make the static method unsafe?
The terms "pure function" and "impure function" are names given in functional programming to what you call "safe" and "unsafe" statics.
This is wrong. There is no such thing as an 'unsafe' static method. The problem is mutating global state, which you can do from anywhere. Mutating global state is bad from a static function, bad from a method, bad from a constructor, and bad anywhere else code can live.
S
S.Lott

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.


I hear what you're saying, but how can something like a Math object encapsulate anything but behaviour?
He just said suspicious, not wrong, and he's absolutely right.
@JonoW: Math is a very special case where there are many stateless functions. Of course, if you're doing Functional programming in Java, you'd have many stateless functions.
G
Grundlefleck

This is really only a follow up to John Millikin's great answer.

Although it can be safe to make stateless methods (which are pretty much functions) static, it can sometimes lead to coupling that is hard to modify. Consider you have a static method as such:

public class StaticClassVersionOne {
    public static void doSomeFunkyThing(int arg);
}

Which you call as:

StaticClassVersionOne.doSomeFunkyThing(42);

Which is all well and good, and very convenient, until you come across a case where you have to modify the behaviour of the static method, and find that you are tightly bound to StaticClassVersionOne. Possibly you could modify the code and it would be fine, but if there was other callers dependent on the old behaviour, they'll need to be accounted for in the body of the method. In some cases that method body can get pretty ugly or unmaintainable if it tries to balance all these behaviours. If you split out the methods you may have to modify code in several places to take account of it, or make calls to new classes.

But consider if you had created an interface to provide the method, and given it to callers, now when the behaviour has to change, a new class can be created to implement the interface, which is cleaner, more easily tested, and more maintainable, and that instead is given to the callers. In this scenario the calling classes don't need to be altered or even recompiled, and the changes are localised.

It may or it may not be a likely situation, but I think it is worth considering.


I argue that this is not only a likely scenario, this makes statics a last resort. Statics make TDD a nightmare too. Wherever you use the static, you can't mock up, you have to know what the input and output is to test an unrelated class. Now, if you change the behavior of the static, your tests on unrelated classes that use that static are broken. Also, it becomes a hidden dependency that you can't pass on the constructor to notify developers of a potentially important dependency.
J
JeeBee

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.


M
Mark Simpson

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/


M
MunkiPhD

That seems to be a reasonable approach. The reason you don't want to use too many static classes/methods is that you end up moving away from object oriented programming and more into the realm of structured programming.

In your case where you are simply transforming A to B, say all we're doing is transforming text to go from

"hello" =>(transform)=> "<b>Hello!</b>"

Then a static method would make sense.

However, if you're invoking these static methods on an object frequently and it tends to be unique for many calls (e.g. the way you use it depends on the input), or it is part of the inherent behavior of the object, it would be wise to make it part of the object and maintaining a state of it. One way to do this would be to implement it as an interface.

class Interface{
    method toHtml(){
        return transformed string (e.g. "<b>Hello!</b>")
    }

    method toConsole(){
        return transformed string (e.g. "printf Hello!")
    }
}


class Object implements Interface {
    mystring = "hello"

    //the implementations of the interface would yield the necessary 
    //functionality, and it is reusable across the board since it 
    //is an interface so... you can make it specific to the object

   method toHtml()
   method toConsole()
}

Edit: One good example of great use of static methods are html helper methods in Asp.Net MVC or Ruby. They create html elements that aren't tied to the behavior of an object, and are therefore static.

Edit 2: Changed functional programming to structured programming (for some reason I got confused), props to Torsten for pointing that out.


I don't think using static methods qualifies as functional programming, so I'm guessing you mean structured programming.
S
Steve Rowe

The reason you are warned away from static methods is that using them forfeits one of the advantages of objects. Objects are intended for data encapsulation. This prevents unexpected side effects from happening which avoids bugs. Static methods have no encapsulated data* and so don't garner this benefit.

That said, if you have no use of internal data, they are fine to use and slightly faster to execute. Make sure you aren't touching global data in them though.

Some languages also have class-level variables which would allow for encapsulation of data and static methods.


o
overslacked

I recently refactored an application to remove/modify some classes that had been initially implemented as static classes. Over time these classes acquired so much and people just kept tagging the new functions as static, since there was never an instance floating around.

So, my answer is that static classes aren't inherently bad but it might be easier to start creating instances now, then have to refactor later.


p
patros

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.


K
Konstantin Chernov

Well, there is no silver bullet of course. Static classes are ok for little utilities/helpers. But using static methods for business logic programming is certainly evil. Consider the following code

   public class BusinessService
   {

        public Guid CreateItem(Item newItem, Guid userID, Guid ownerID)
        {
            var newItemId = itemsRepository.Create(createItem, userID, ownerID);
            **var searchItem = ItemsProcessor.SplitItem(newItem);**
            searchRepository.Add(searchItem);
            return newItemId;
        }
    }

You see a static method call to ItemsProcessor.SplitItem(newItem); It smells cause

You dont have explicit dependency declared and if you don't dig into code you might overlook the coupling between your class and static method container

You can't test BusinessService isolating it from ItemsProcessor (most test tools do not mock static classes) and it makes unit testing impossible. No Unit Tests == low quality


B
Bill K

I used to go back and forth between a class with a bunch of static methods and a singleton. Both solve the problem, but the singleton can be much more easily replaced with more than one. (Programmers always seem so certain that there will only be 1 of something and I found myself wrong enough times to completely give up on static methods except in some very limited cases).

Anyway, the singleton gives you the ability to later pass something into the factory to get a different instance and that changes the behavior of your entire program without refactoring. Changing a global class of static methods into something with different "backing" data or a slightly different behavior (child class) is a major pain in the butt.

And static methods have no similar advantage.

So yes, they are bad.


L
Lucero

As long as not internal state comes into play, this is fine. Note that usually static methods are expected to be thread-safe, so if you use helper data structures, use them in a thread-safe manner.


A
Adam Crume

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.


A
Andrey Chaschev

If it's a utility method, it's nice to make it static. Guava and Apache Commons are built upon this principle.

My opinion on this is purely pragmatic. If it's your app code, static methods are generally not the best thing to have. Static methods have serious unit-testing limitations - they can't be easily mocked: you can't inject a mocked static functionality into some other test. You also can't usually inject functionality into a static method.

So in my app logic I usually have small static utility-like method calls. I.e.

static cutNotNull(String s, int length){
  return s == null ? null : s.substring(0, length);
}

one of the benefits is that I don't test such methods :-)


J
John

Static methods are generally a bad choice even for stateless code. Instead make a singleton class with these methods that is instantiated once and injected into those classes wanting to use the methods. Such classes are easier to mock and test. They are far more object oriented. You can wrap them with a proxy when needed. Statics make OO much harder and I see no reason to use them in almost all cases. Not 100% but almost all.