8

I understand the concept of an object, and as a Java programmer I feel the OO paradigm comes rather naturally to me in practice.

However recently I found myself thinking:

Wait a second, what are actually the practical benefits of using an object over using a static class (with proper encapsulation and OO practices)?

I could think of two benefits of using an object (both are significant and powerful):

  1. Polymorphism: allows you to swap functionality dynamically and flexibly during runtime. Also allows to add new functionality 'parts' and alternatives to the system easily. For example if there's a Car class designed to work with Engine objects, and you want to add a new Engine to the system that the Car can use, you can create a new Engine subclass and simply pass an object of this class into the Car object, without having to change anything about Car. And you can decide to do so during runtime.

  2. Being able to 'pass functionality around': you can pass an object around the system dynamically.

But are there any more advantages to objects over static classes?

Often when I add new 'parts' to a system, I do so by creating a new class and instantiating objects from it.

But recently when I stopped and thought about it, I realized that a static class would do just the same as an object, in a lot of the places where I normally use an object.

For example, I'm working on adding a save/load-file mechanism to my app.

With an object, the calling line of code will look like this: Thing thing = fileLoader.load(file);

With a static class, it would look like this: Thing thing = FileLoader.load(file);

What's the difference?

Fairly often I just can't think of a reason to instantiate an object when a plain-old static-class would act just the same. But in OO systems, static classes are fairly rare. So I must be missing something.

Are there any more advantages to objects other from the two that I listed? Please explain.

EDIT: To clarify. I do find objects very useful when swapping functionality, or passing data around. For example I wrote an app that makes up melodies. MelodyGenerator had several subclasses that create melodies differently, and objects of these classes were interchangable (Strategy pattern).

The melodies were objects too, since it's useful to pass them around. So were the Chords and Scales.

But what about 'static' parts of the system - that aren't going to be passed around? For example - a 'save file' mechanism. Why should I implement it in an object, and not a static class?

22
  • So, instead of an object with fields and perhaps methods, no objects, no records, just a whole lot of scalar values passed to and returned from static methods? Edit: Your new example suggests otherwise: Objects, just without instance methods? Otherwise, what's Thing?
    – user7043
    Jun 3, 2014 at 21:28
  • What happens when you need to swap your FileLoader for one that reads from a socket? Or a mock for testing? Or one that opens a zip file? Jun 3, 2014 at 21:28
  • similar, but less broad questions were asked and answered few times before: Can't I just use all static methods?, Make methods that do not depend on instance fields, static?, How to deal with static utility classes when designing for testability etc
    – gnat
    Jun 3, 2014 at 21:29
  • 1
    @delnan Okay I figured out a question that the answer will help me understand: why would you implement a 'static' part of the system as an object? Like the example in the question: a save-file mechanism. What do I gain from implementing it in an object?
    – Aviv Cohn
    Jun 3, 2014 at 21:40
  • 1
    The default should be to use a non-static object. Only use static classes if you feel that it really expresses your intention better. System.Math in .NET is an example of something that makes a lot more sense as a static class: you're never going to need to swap it out or mock it and none of the operations could logically be made part of an instance. I really don't think your 'saving' example fits that bill. Jun 4, 2014 at 6:42

6 Answers 6

14

lol you sound like a team that I used to work on ;)

Java (and probably C#) certainly supports that programming style. And I work with people who's first instinct is, "I can make that a static method!" But there are some subtle costs that catch up with you over time.

1) Java is an Object Oriented language. And it drives the functional guys nuts, but really it holds up pretty well. The idea behind OO is bundling functionality with state to have small units of data and functionality that preserve their semantics by hiding state and exposing only the functions that make sense in that context.

By moving to a class with only static methods, you're breaking the "state" part of the equation. But the state still has to live somewhere. So what I've seen over time is that a class with all static methods begins to have more and more complicated parameter lists, because the state is moving from the class and into the function calls.

After you make a class with all static methods, run through and just survey how many of those methods have a single, common parameter. It's a hint that that parameter should either be the containing class for those functions, or else that parameter should be an attribute of an instance.

2) The rules for OO are pretty well understood. After a while, you can look at a class design and see if it meets criteria like SOLID. And after lots of practice unit testing, you develop a good sense of what makes a class "right-sized" and "coherent". But there aren't good rules for a class with all static methods, and no real reason why you shouldn't just bundle up everything in there. The class is open in your editor, so what the heck? Just add your new method there. After a while, your application turns into a number of competing "God Objects", each trying to dominate the world. Again, refactoring them into smaller units is highly subjective and hard to tell if you've got it right.

3) Interfaces are one of the most powerful features of Java. Class inheritance has proven to be problematic, but programming with interfaces remains one of the most powerful tricks of the language. (ditto C#) All-static classes can't put themselves into that model.

4) It slams the door on important OO techniques that you can't take advantage of. So you may work for years with only a hammer in your toolbox, without realizing how much easier things would have been if you'd had a screwdriver, too.

4.5) It creates the hardest, most unbreakable compile-time dependencies possible. So, for example if you have FileSystem.saveFile() then there is no way of changing that, short of faking out your JVM at run time. Which means that every class that references your static function class has a hard, compile-time dependency on that specific implementation, which makes extension almost impossible, and complicates testing tremendously. You can test the static class in isolation, but it becomes very difficult to test the classes that refer to that class in isolation.

5) You'll drive your co-workers crazy. Most of the professionals I work with take their code seriously, and pay attention to at least some level of design principles. Setting aside the core intent of a language will have them pulling their hair out because they'll be constantly refactoring the code.

When I'm in a language, I always try to use a language well. So, for example, when I'm in Java, I use good OO design, because then I'm really leveraging the language for what it does. When I'm in Python, I mix module-level functions with occasional classes -- I could only write classes in Python, but then I think I wouldn't be using the language for what it's good at.

Another tactic is to use a language badly, and them complain about all the problems its causing. But that applies to pretty much any technology.

The key feature of Java is managing complexity in small, testable units that hang together so they're easy to understand. Java emphasizes clear interface definitions independent of the implementation -- which is a huge benefit. That's why it (and other similar OO languages) remain so widely used. For all the verbosity and ritualism, when I'm done with a big Java app, I always feel like the ideas are more cleanly separated in code than my projects in a more dynamic language.

It's a hard one, though. I've seen people get the "all static" bug, and it's kind of hard to talk them out of it. But I've seen them have a big sense of relief when they get over it.

13
  • You talk mainly about keeping things small and modular, and keeping state and functionality together. There's nothing that stops me from doing this with static classes. They can have state. And you can encapsulate them with getters-setters. And you can divide the system to small classes that encapsulate functionality and state. All of this applies both to classes and objects. And this exactly is my dilemma: say I'm adding new functionality to my app. Obviously it's going to go in a separate class to obey SRP. But why exactly should I instantiate this class? This is what I don't understand.
    – Aviv Cohn
    Jun 3, 2014 at 21:52
  • I mean: sometimes it's clear why I want objects. I'll use the example from the edit to my question: I had an app that makes up melodies. There were different Scales I could plug in the MelodyGenerators to generate different melodies. And the Melodies were objects, so I could put them in a stack and go back to play old ones, etc. In cases like these it's clear to me why I should use objects. What I don't understand is why I should use objects to represent 'static' parts of the system: e.g. a save-file mechanism. Why shouldn't this just be a static class?
    – Aviv Cohn
    Jun 3, 2014 at 21:55
  • A class with static methods has to externalize it's state. Which, sometimes, is a very important factoring technique. But most of the time you want to encapsulate state. A concrete example: years ago a co-worker wrote a "date picker" in Javascript. And it was a nightmare. The cusomers complained about it constantly. So I refactored it into "calendar" objects, and suddenly I was instantiating multiple of them, putting them side-by-side, jumping between months and years, etc. It was so rich that we actually had to turn features off. Instantiation gives you that scale.
    – sea-rob
    Jun 3, 2014 at 21:57
  • 3
    "The idea behind OO is bundling functionality with state to have small units of data and functionality that preserve their semantics by hiding state and exposing only the functions that make sense in that context." This is fundamentally wrong. OO does not imply mutable state and abstraction is not exclusive to OO. There's two ways of achieving abstraction and objects are only one of them (the other being abstract data types).
    – Doval
    Jun 4, 2014 at 12:06
  • 1
    @Doval I don't understand why every discussion of OO must necessarily turn into a discussion of functional programming.
    – sea-rob
    Jun 4, 2014 at 14:24
6

You asked:

But are there any more advantages to objects over static classes?

Before this question, you listed Polymorphism and being passed around as two benefits of using Objects. I want to say that those are the features of the OO paradigm. Encapsulation is another feature of the OO paradigm.

However, those are not the benefits. The benefits are:

  1. Better abstractions
  2. Better maintainability
  3. Better testability

You said:

But recently when I stopped and thought about it, I realized that a static class would do just the same as an object, in a lot of the places where I normally use an object.

I think you have a valid point there. At its core, programming is nothing but transformation of data and creation of side effects based on data. Sometimes transforming data requires auxiliary data. Other times, it does not.

When you are dealing with the first category of transformations, the auxiliary data has to be either passed in as input or stored somewhere. An Object is the better approach that static classes for such transformations. An Object can store the auxiliary data and use it at the right time.

For the second category of transformations, a static class is as good as an Object, if not better. Mathematical functions are classic examples of this category. Most of standard C library functions fall into this category too.

You asked:

With an object, the calling line of code will look like this: Thing thing = fileLoader.load(file);

With a static class, it would look like this: Thing thing = FileLoader.load(file);

What's the difference?

If FileLoader does not, ever, need to store any data, I would go with the second approach. If there is a smidgen of a chance that it will need auxiliary data to perform the operation, the first approach is a safer bet.

You asked:

Are there any more advantages to objects other from the two that I listed? Please explain.

I listed the benefits (advantages) of using the OO paradigm. I hope they are self explanatory. If not, I'll be glad to elaborate.

You asked:

But what about 'static' parts of the system - that aren't going to be passed around? For example - a 'save file' mechanism. Why should I implement it in an object, and not a static class?

This is an example where a static class simply won't do. There are many ways to save your application data to a file:

  1. Save it in a CSV file.
  2. Save it in an XML file.
  3. Save it in a json file.
  4. Save it as a binary file, with direct dump of the data.
  5. Save it directly to some table in a database.

The only way to provide such options is to create an Interface and create Objects that implement the interface.

In Conclusion

Use the right approach for the problem at hand. It is better not to be religious about one approach vs the other.

1
  • Also, if many different object types (classes) need to be saved (e.g. Melody, Chord, Improvisations, SoundSample and others), then it will be economical to simplify the implementation via abstractions.
    – rwong
    Jun 4, 2014 at 8:50
5

It depends on the language and the context, sometimes. For example, PHP scripts used for servicing requests always have one request to service and one response to generate throughout the entire lifetime of the script, so static methods to act on the request and generate a response may be appropriate. But in a server written on Node.js, there may be many different requests and responses all at the same time. So the first question is -- are you sure that the static class really corresponds to a singleton object?

Secondly, even if you have singletons, using objects allows you to take advantage of polymorphism using techniques like Dependency_injection and Factory_method_pattern . This is often used in various Inversion_of_control patterns, and useful for creating mock objects for testing, logging, etc.

You mentioned the advantages above, so here is one you didn't: inheritance. Many languages have no ability to override static methods the same way that instance methods can be overridden. In general, it's much harder to inherit and override static methods.

1
  • Thanks for answering. In your opinion: when creating a 'static' part of the system, something that's not going to be 'passed around' - for example the part of the system that plays sounds, or the part of the system that saves data to a file: should I implement it in an object or in a static class?
    – Aviv Cohn
    Jun 3, 2014 at 22:34
3

In addition to Rob Y's post


As long as the functionality of your load(File file) can be clearly seperated from all the other functions that you use, it is fine to use a static method/class. You externalize the state (what is not a bad thing) and you also don't get redundancy as you can for example partial apply or curry your function so that you don't have to repeat yourself. (that is actually the same or similar to using a factory pattern)

However, as soon as two of these functions start to have a common use you want to be able to somehow group them. Imagine you not only have a load function but also a hasValidSyntax function. What will you do?

     if (FileLoader.hasValidSyntax(myfile))
          Thing thing = FileLoader.load(myfile);
     else
          println "oh noes!"

See the two references to myfile here? You start to repeat yourself because your externalized state has to be passed for every call. Rob Y has described how you internalize the state (here the file) so that you can do it like this:

     FileLoader myfileLoader = new FileLoader(myfile)
     if (myfileLoader.hasValidSyntax())
          Thing thing = myfileLoader.load();
     else
          println "oh noes!"
1
  • Having to pass in the same object twice is a superficial symptom; the actual problem it might indicate is that some work is performed redundantly - the file might have to be opened twice, parsed twice, and closed twice, if hasValidSyntax() and load() are not allowed to reuse state (the result of a partially parsed file).
    – rwong
    Jun 4, 2014 at 8:42
2

A major issue when using static classes is that they force you to hide your dependencies, and force you to depend on implementations. In the following constructor signature, what are your dependencies:

public Person(String name)

Well, judging from the signature, a person just needs a name, right? Well, not if the implementation is:

public Person(String name) {
    ResultSet rs = DBConnection.getPersonFilePathByName(name);
    File f = FileLoader.load(rs.getPath());
    PersonData.setDataFile(f);
    this.name = name;
    this.age = PersonData.getAge();
}

So a person isn't just instantiated. We actually pull data from a database, which gives us a path to a file, which then must be parsed, and then the real data we want has to be teased out. This example is clearly over the top, but it proves the point. But wait, there can be so much more! I write the following test:

public void testPersonConstructor() {
    Person p = new Person("Milhouse van Houten");
    assertEqual(10, p.age);
}

This should pass though, right? I mean, we encapsulated all that other stuff, right? Well, actually it blows up with an exception. Why? Oh, yes, the hidden dependencies we didn't know about have global state. The DBConnection needs initialized and connected. The FileLoader needs initialized with a FileFormat object (like XMLFileFormat or CSVFileFormat). Never heard of those? Well, that's the point. Your code (and your compiler) can't tell you that you need these things because the static calls hide those dependencies. Did I say test? I meant that the new junior developer just shipped something like this in your most recent release. After all, compiled = works, right?

Additionally, say you're on a system that doesn't have a MySQL instance running. Or, say you are on a Windows system but your DBConnection class only goes out to your MySQL server on a Linux box (with Linux paths). Or, say you are on a system where the path returned by the DBConnection is not read/write for you. That means that trying to run or test this system under any of these conditions will fail, not because of a code error, but because of a design error that limits the flexibility of the code and ties you to an implementation.

Now, let's say, we want to log every call to the database for one specific Person instance, one that goes through a certain, troublesome path. We could put logging into DBConnection, but this will log everything, putting a lot of clutter in and making it hard to distinguish the particular code path we want to trace. If, however, we are using dependency injection with a DBConnection instance, we could simply implement the interface in a decorator (or extend the class, as we have both options available with an object). With a static class, we cannot inject the dependency, we cannot implement an interface, we cannot wrap it in a decorator, and we cannot extend the class. We are only able to call it directly, somewhere hidden deep in our code. Thus we are forced to have a hidden dependency on an implementation.

Is this always bad? Not necessarily, but it might be better to reverse your point of view and say "Is there a good reason this should not be an instance?" rather than "Is there a good reason this should be an instance?" If you can truly say that your code will be as unwavering (and stateless) as Math.abs(), both in its implementation and in the manner it will be used, then you can consider making it static. Having instances over static classes, though, gives you a world of flexibility that is not always easy to recapture after the fact. It also can give you more clarity into the true nature of your code's dependencies.

5
  • Hidden dependencies are extremely good point. I almost forgot my idiom that "Quality of code should be judged from how it is used, not how it is implemented."
    – Euphoric
    Jun 5, 2014 at 7:11
  • But hidden dependencies can exist in a non-static class too right? So I don't see why this would be a drawback of static classes but not of non-static classes.
    – valenterry
    Jun 5, 2014 at 23:14
  • @valenterry Yes, we can hide non-static dependencies as well, but the key point is that hiding non-static dependencies is a choice. I could new up a bunch of objects in the constructor (which is not generally advisable), but I can also inject them. With static classes, there is no way to inject them (not easily in Java anyway, and that would be a whole other discussion for languages that allow that), so there is no choice. That's why I highlight the idea of being forced into hiding dependencies so heavily in my answer.
    – cbojar
    Jun 5, 2014 at 23:45
  • But you can inject them every time you call the method by providing them as arguments. So you are not forced to hide them but forced to make them explicit so that everyone sees "aha, that method depends on these arguments" rather than "aha, that method depends on these (part of) arguments and some hidden interal state I dont know".
    – valenterry
    Jun 6, 2014 at 0:35
  • In Java, to the best of my knowledge, you cannot supply a static class as a parameter without going through a whole reflection rigmarole. (I tried every way I can think of. If you have a way, I'd like to see it.) And if you do choose to do that, you are essentially either subverting the built-in type system (the parameter is a Class, then we make sure it is the right class) or you are duck typing (we make sure it responds to the right method). If you want to do the latter, then you should re-evaluate your language choice. If you want to do the former, instances do just fine and are built in.
    – cbojar
    Jun 10, 2014 at 0:49
1

Just my two cents.

For your question:

But what about 'static' parts of the system - that aren't going to be passed around? For example - a 'save file' mechanism. Why should I implement it in an object, and not a static class?

You can ask yourself if the mechanism of the part of the system that plays sounds, or the part of the system that saves data to a file WILL CHANGE. If your answer is yes, that indicate that you should abstract them using abstract class / interface. You may ask, how can I know the future things? Definitely, we cann't. So, if the thing is stateless, you can use 'static class' such as java.lang.Math, otherwise, use object-oriented approach.

1
  • There is a tongue-in-cheek advice: Three strikes and you refactor, which suggests that one can hold off until the change is actually needed. What I mean by "change" is: more than one type of object need to be saved; or needing to save to more than one file format (storage type); or a drastic increase in complexity of saved data. Of course if one is fairly certain that the change will happen soon, one can incorporate a more flexible design up-front.
    – rwong
    Jun 4, 2014 at 8:57

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