-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.

closed as unclear what you're asking by gnat, Kilian Foth, Bart van Ingen Schenau, coredump, JacquesB Feb 11 '16 at 12:38

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 4
    Possible duplicate of Can't I just use all static methods? – gnat Feb 11 '16 at 8: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 9: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 0: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
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.

  • 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
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);
  • 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
-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.

  • 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 9: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.

  • 1
  • @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

Not the answer you're looking for? Browse other questions tagged or ask your own question.