-2

Rewrite of the Question: Is there a technical reason why we are not using static methods instead of instance methods.

Technical reasons are for example: Performance or added type-safety.

I reason that with static methods and only pure data classes you will get better type safety and more maintainable code.


Why do we add methods to classes?

When I look at what the compiler does we can infer that an instance method on a class gets transformed into:

public static class Foo {
    public static int Something(object this) { ... }
}

This shows us that even "under the hood" we just pass the data as a parameter to our methods, or more pointedly, functions.

What is the reasoning behind having instance methods?

My research has found three key reasons for adding instance methods:

  • Convention, we've been taught to do it that way
  • Ignorance, we do not know of another way
  • Convenience, easier to find functionality, basically a way to group functionality.

I find all three reasons shaky at best. I think we can all agree that the first two reasons are not really valid reasons, this leaves us with the third reason, Convenience, but when you look at the calling code of an instance method you see something strange happen:

var o = new MyClass(..some parameters...);
o.Something(...some parameters...);

We have split our function signature over two lines. We have basically lost our chance of really type-checking changes made to our system. And comparing this to something like currying is not a valid comparison. Would using classes to express data and using functions (static classes with static methods) not be a better and even more convenient way to group data?

We could rewrite the previous calling code to:

StaticLib.Something(...some parameters...);

And just be done with it. Using the code would be easier to test and to implement and grouping the code is easy as well. Just use basic segmentation criteria on your collection of functions. You can even automate this grouping when you take types and signatures into account...


EDIT: As stated by @Daniel T. I seem to be coming down hard on convention. This is not what I want to convey. Convention is great when working in a big project or working with colleagues. Convention is also a really great way to bottle change, please read this next edit not as an attack on convention in projects but as an attack on convention as an argument for reasoning.


EDIT: I am not talking about OOP vs FP, we should all agree that polyglot solutions are best. I am not interested in answers like:

  • Most programmers expect their code to look like....
  • It is convention
  • I've been taught to do it like that...

Those are answers based on convention and dogma and will never result in better ways of thinking or coding.

I am interested in proofs and solid reasoning. The accepted answer in the so called duplicate reduces the question to two things:

  • It does not feel OO enough
  • I don't like it

This is not a proof and is not an answer to the question. Furthermore I do not ask the same question so it's doubtful it's a duplicate.


EDIT: This is not a duplicate. I'm not interested in inheritance or other OOP aspects. Just about the quality and readability of the code.

Baudin999
  • 19
  • 5
  • 4
    Possible duplicate of [Can't I just use all static methods?](http://programmers.stackexchange.com/questions/98083/cant-i-just-use-all-static-methods) – gnat Feb 11 '16 at 08:42
  • Not a duplicate, see the edit. Basically, I'm not interested in OOP, just code quality. Not working towards a "FP vs OOP" debate, just about organisation or your code, maintainability etc etc. – Baudin999 Feb 11 '16 at 09:57
  • 2
    Not all OOP languages bind methods to a class. Generic functions in Lisp are use to perform multiple-dispatch and are thus not tied to a specific class. You would call it like so `(do-something x y z)` and the effective method being called would depend on `x`, `y` `z`. But if you work in Java or C++, data and functions are encapsulated in a single entity which is the class, and here you have single-dispatch on the implicit `this`, which is read as the *subject* of an operation, separate from the *arguments*. This is neither worse nor better, it just is (I know which I prefer, though ;-). – coredump Feb 11 '16 at 12:57
  • The requirement for any paradigm or high-level language is about humans managing complexity. There is no technical requirement for any high level language nor their associated paradigms, The reality is that everything you could ever hope to accomplish in those languages is also possible by writing machine code directly. (or in the case of managed languages, the Bytecode/MSIL/etc.). Programmers cost real money, and having them re-invent the wheel in assembly/IL is prohibitively expensive for most businesses. Better to save money with an OO model in a high-level language like C# instead. – Ben Cottrell Feb 14 '16 at 00:58
  • @BenC This is a very flawed analogy. First, I'm not suggesting anyone write IL. Secondly. Because IL is hard/expensive to write we should write OO in C#? With the same argumentation I could say: "Because IL is hard/expensive we should wear monkey t-shirts." You have give no foundation or proof whatsoever as to why C# and OO are good. – Baudin999 Feb 15 '16 at 12:57
  • You are explicitly seeking a 'technical requirement' for OO, and I'm trying to explain that such a technical requirement does not exist. The requirement for OO and many other high-level constructs are based around human needs, not technical ones. In other words, your underlying hardware (or Virtual Machine running bytecode/IL) does not care about OO, and OO is not able to "do anything" which cannot be done without OO. – Ben Cottrell Feb 15 '16 at 13:30
  • To put it another way, your question appears to be based on the premise that OO was created for technical reasons; however the problem is that's a false proposition. My suggestion is to approach your question with the premise that OO was created for human reasons instead. – Ben Cottrell Feb 15 '16 at 13:48
  • I understand your comment now. You have a valid point. OO was created to give structure to code, I am not denying that this is the case. I'm simply trying to look at why "methods should belong to a class." The way I see it this has got nothing to do with OO, furthermore, if you take SOLID to its logical conclusion you end up with a lot of 1 method interfaces implemented on a single piece of data. This logical conclusion wrought the idea of static methods. And when you pass everything in as a parameter you get added type safety. On top of that you get a more FP style and polyglot is the future. – Baudin999 Feb 16 '16 at 08:35
  • I know it does not seem that way, but my question is not out of ignorance but out of a drive to create better software. I have been doing OO, TDD and SOLID for years and years. I've been doing FP for about five years now (mostly F#); I'm trying to synthesize the best of both worlds into an architecture which can unite experts and novices from both fields. – Baudin999 Feb 16 '16 at 08:38

4 Answers4

4

Don't underestimate the power of convention. If everybody does things the same way, it makes big programmes more maintainable.


Your example StaticLib.Something(...some parameters...); only looks simpler because you've glossed over the details. You haven't actually stated where the MyStruct data is initialised, nor passed it into the Something function.

I'm assuming that you're doing structured programming, rather than just procedural code with thousands of variables all over the place. If we're going to do structured programming, we need to store the program data in a collection of structures/classes. All the popular coding guidelines insist that data must be initialised before it is used. So let us assume that we have a structure MyClass to hold data about something. We need to create the structure and initialise to to a known state.

MyClass o = StaticLib.CreateMyClass(... initial state ...);

or perhaps

MyClass o; StaticLib.InitMyClass(o, ... initial state...);

Now we want to do something with o.

StaticLib.Something(o, ...some parameters...);

Put it together, and you get

MyClass o = StaticLib.CreateMyClass(... initial state ...);
StaticLib.Something(o, ...some parameters...);

At which point, you might as well do it the "object oriented way", and just write

MyClass o = new MyClass(... initial state ...);
o.Something(... some parameters...);

Object Oriented programming also naturally gives a better program structure. The definition of the data structure is always closely associated with the functions that operate on it. That's easier to maintain than a definition of a data structure in one place, and a library of functions that operate on it somewhere else.


And finally, it just looks nicer to write

lion.Roar(LOUDLY);

than

Animals.Roar(lion, LOUDLY);
Simon B
  • 9,167
  • 4
  • 26
  • 33
  • Great response, let me try and dig a bit deeper. I'm not saying that convention within a project or maybe within a set of programs is a bad thing. My point is that convention tends to get in the way of curiosity and progress. You are right in assuming that I'm trying to do structured programming. You are also right in assuming that if you take the initializer *(and this could still be `new MyData()` I'm not opposed to classes and ctors especially for data)* you can count the same number of lines. What I'm advocating is the typesafety you get from having all params in place. (will continue...) – Baudin999 Feb 11 '16 at 12:22
  • (continued) outweighs the benefit of having the methods in the same class (you probably mean in the same file because with for example: *partial classes* you can still save them all over the place). Another thing to notice is that with `Animal.Roar(lion....)` it is even more clear from your code what is happening. This one line gives you a lot more insight into what this code does. `lion.Road(....` *hides* structure while the first example *bares* structure. One last thing, separating the data from the functions feels almost *more* OO in a sense that transformations are now externalized. – Baudin999 Feb 11 '16 at 12:28
1

One big reason is polymorphism (not inheritance, which is just one form of polymorphism).

Consider the following example:

interface Payable {
  ...
  double calculatePay();
  ...
}

public class HourlyEmployee implements Payable {
  ...
  public double calculatePay() {
    return hourlyRate * timeCards.totalHoursWorked();
  }
  ...
}

public class SalariedEmployee implements Payable {
  ...
  public double calculatePay() {
    return salary;
  }
  ...
}

public class PayRun {
  ...
  private PaySlip buildPaySlip(Payable payable) {
    PaySlip paySlip = new PaySlip();
    ...
    paySlip.setGrossPay(payable.calculatePay());
    ...
    return paySlip;
  }
  ...
}

If instance methods were just functions receiving the object as a hidden parameter, the code above wouldn't work. The caller of the method calculatePay would need to know which kinds of Payable objects exist in order to call the right function. With OO, that knowledge is not necessary, thus the PayRun object is capable of building PaySlips for any kind of Payable object. We can add CommissionedEmployee or any other kind without changing the core of the payroll process.

Of course, polymorphism is not even close to being exclusive to OO. However, it is a concept baked into the core of the paradigm (and related languages), making it a first-class feature instead of just something you can achieve (usually by doing things in unfriendly and unidiomatic ways, like virtual tables in C). However, I criticise statically typed languages like Java and C++ as being too constrictive about polymorphism. Languages that cheaply support duck-typing are better subjects of study to understand the reasoning behind OO.

MichelHenrich
  • 6,225
  • 1
  • 27
  • 29
  • Thank you for the answer. Polymorphism seems like a good candidate to debunk my initial theory, but let's examine it: here we're talking about subtype polymorphism which is different from parametric polymorphism. And the only reason why you would want to use polymorphism here is basically because you like `calculatePay` to have no parameters and be called on **every** `IPayable` type (sorry, I added the I). Every function body is different. Would it not be as reasonable to `match` on type and call a different function per option? This would make it easier to reason about our code. – Baudin999 Feb 11 '16 at 12:53
  • Matching on type is sometimes a good way to organize the code, but only if all options would change together. If we use type matching in my example (I'm imagining a switch-case statement) it would be necessary to change the same part of the code in order to add new and unrelated functionallity. With the polymorphic way, the changes are isolated in their own classes. This allows for parallel development, isolated deployments, and so on. With the type-matching solution, the caller and the callee would have to be changed and redeployed together. Basically, it breaks the S and the O from SOLID. – MichelHenrich Feb 11 '16 at 13:10
  • Although higher order functions are a great way to solve Polymorphic requirements and although I think that higher order functions might improve type-safety compared to subtype polymorphism...I do not think that passing functions as parameters makes code "easier to read or understand". Polymorphism is indeed a reason to *not* use static methods! Thank you for the answer and I appreciate the effort. – Baudin999 Feb 12 '16 at 13:36
-2

I think it might be Java related, where everything has to be in a class (even when, as you point out, its not ever in an instance of a class).

C++ allows normal functions, but also maintains the static keyword that used to constrain a variable or function's scope to the containing file. C++ kept this meaning but modified the scope to the containing class, even this its more a namespace thing as you still have to allocate the storage space for it externally.

This is a good reason why its done - to limit definition scope, so someone reading your code knows that a static variable or function is only referred to inside (or with relation to) a particular class, even though its not tied to individual instances.

gbjbaanb
  • 48,354
  • 6
  • 102
  • 172
  • 1
    Your last paragraph confuses me. Why would parameters limit your scope less? Is it just the fact that instance methods are in the same "class"? Parameters are type checked. I can add instance methods that do something *completely different* in a class and only have convention to point out I did something wrong... – Baudin999 Feb 11 '16 at 09:59
  • I'm just saying its scoped to the class name. Nothing to do with type checking, just a logical place to put related functionality. – gbjbaanb Feb 11 '16 at 10:23
  • In my question I'm trying to dig a bit deeper. I do not accept the premise that *it's a logical place to put related functionality*. In my question this would fall under *Convenience* which I'm trying to debunk. I am looking for an argument which is testable and fundamental. – Baudin999 Feb 11 '16 at 10:57
-2

Once you remove dynamic dispatch from the mix, the answer is that there is no reason, other than convention, that we do it that way.

In English, we read/write from left to right. Why? No reason other than convention. I get the impression that you are being much too quick in discarding convention. Conventional code is easier to write, easer to read and easier to maintain...

a = b.c(d, e).f(g).h()

vs

a = h(f(c(b, d, e), g))

The former reads more naturally from left to right than the latter. Since we English speakers are trained to read from left to right from a very young age, such construction makes the former easier to read and write.

In the former, there are no nested parentheses. Because of that, we are less likely to put in too many, or too few close parens, and we can more easily see that the parens are balanced. This makes the former easier to write and read.

When code is easier to read, it's easier to maintain and understand. When it is easier to write, it is more likely to be written correctly the first time.

Do your friends, including future you, a favor and make your code easier to read and write.

Daniel T.
  • 3,013
  • 19
  • 24
  • 1
    [Why is English written and read left to right?](https://english.stackexchange.com/questions/7304/why-is-english-written-and-read-left-to-right) – coredump Feb 11 '16 at 12:36
  • @carlos-kelkboom: In your question you said, "... answers based on convention and dogma and will never result in better ways of thinking or coding." Please remember also that fighting convention for no good reason other than to fight is even less likely to result in better ways of thinking or coding. We don't have to prove that the conventional way is better, you have to prove that your unconventional way is better. Don't shift the burden of proof onto the norm. That's a fallacy. – Daniel T. Feb 11 '16 at 12:45
  • But keeping an open mind and looking at other options, maybe trying a few silly ones, does result in overall improvement. As I said, conventions in a project or over several projects combined is great! But conventions kill progress when you are not allowed to reason beyond them. – Baudin999 Feb 11 '16 at 12:49
  • By all means, reason beyond convention! In your question you weren't reasoning beyond convention, you were flouting convention for no good reason. There's a huge difference. – Daniel T. Feb 11 '16 at 12:51
  • You sir are completely right! I flaunted convention in no uncertain words but I did not mean to. Convention is great when "doing" and I've said so in comments. I lost track of "what I said where". – Baudin999 Feb 11 '16 at 12:56