Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having pairs of static and instanced methods that perform the same tasks?

While developing a two-dimensional vector class as part of a math library, I'm considering having static and instance method pairs for stylistic and usability reasons. That is, two equivalent functions but one is static & non-mutating, and the other is instanced & mutating. I know I'm not the first person to consider this problem (See here, for example) but I haven't found any information that directly addresses it.

Pros of having static and instance method pairs:

  • Some people prefer to use one or the other and in some cases being able to choose makes code easier to read.
  • It is implied that static methods are not mutating when both static and instanced methods are provided. This can make the calling code much clearer, e.g.:

    someVector = Vector2d.add(vec1, vec2);
    someVector = (new Vector2d(vec1)).add(vec2); // does the same thing although more convoluted.
    
    // similarly adding directly to a vector is simpler with a mutator method.
    someVector.add(vec2);
    someVector = Vector2d.add(someVector, vec2);
    

    This is especially important when long chains of function calls are used, which is common with vectors.

  • In-place operations can be faster computationally than creating a new instance for every operation. The user decides when performance is important. For users of a Vector class, performance may be important as vectors are frequently used in computationally expensive code.

Pros of having only static or instance methods, but not both:

  • No significant code redundancy. Easier to maintain.

  • Less bloat. The javadocs will be almost half the size.

  • Not necessary to inform users that static methods never mutate and non-getter instanced methods always mutate.

How frowned upon is having static/instance method pairs? Is it used in any major libraries?

Is the pattern "static methods don't mutate, instance methods do" widely known?

like image 518
tailattention Avatar asked Nov 23 '14 14:11

tailattention


2 Answers

I think your concept of providing both static/immutable and instance/mutable methods is a good one. I think the distinction is easy to explain and will be easy for the API users to understand and remember.

I think your API implementation code will not have redundant business logic. You will find that that you repeat a pattern where the static implementation creates a new instance and calls the instance method on that new instance.

Given that I am lazy, I would look at building a bit of infrastructure that would auto-generate the static methods, their javadoc and their unit tests at compile-time. This would be overkill if you have 10 methods, but becomes a big win if you have 1,000 methods.

like image 99
Rob Avatar answered Nov 17 '22 12:11

Rob


On the first part, "static methods don't mutate", that's widely used in OOP. I haven't heard of it being expressed explicitly. But it is common sense: "If you change an object, why would the method be static if it could be an instance method?" So I completely agree with the "static methods don't mutate".

On the second part, "instance methods do [mutate]", that's actually not as widely used. It rather depends on whether you decide your design to apply immutability or mutability. Examples from the Java API: java.lang.String is immutable, java.util.Date is mutable (most likely by accident / bad design), java.lang.StringBuilder is mutable intentionally (that's its purpose). Mutability can lead to defensive cloning in order to protect the code from mutation bugs. Whether this really is a problem depends on a few things:

  • Is it an API others will use? You never know how they will use your code... IMO it's more important to protect API code from mutation bugs than normal code.
  • How good is the unit test coverage? Would your unit tests find all the mutation bugs that might sneak in? If you follow TDD properly (Uncle Bob's 3 Laws of TDD), and it's non-API code, mutation bugs are very unlikely to sneak in without being instantly discovered.
  • If you have code that has to protect itself against mutation bugs using defensive cloning, how often is that code called? If defensive clones are created frequently, it might be better to use immutable objects than mutable objects. Basically this is the call of the number of calls of read-only methods (that would eventually defensively clone) of associating classes vs. the number of calls of mutator methods on the class itself.

Personally, I prefer immutable objects, I'm a fan of final (if I could change Java, I would make final the default for all fields and variables, and introduce a keyword var to make them non-final), and I try to do functional programming in Java, although it is not a functional programming language, as much as possible. From my experience I know that I spend significantly less time debugging my code than others (actually I run the Java debugger maybe twice a year or so). I do not have enough empirical data and proper analysis for creating any kind of "causal relationship" between experience, immutability, functional programming and correctness, therefore I will only say I believe that immutability and functional programming help for correctness, and you will have to come up with your own judgement on this.

Concluding on the second part, "instance methods do [mutate]" is the widely used assumption in case the object is mutable anyway, otherwise instance methods would clone.

like image 41
Christian Hujer Avatar answered Nov 17 '22 12:11

Christian Hujer