One concept which pops up when dealing with the basics of object-oriented programming (OOP) is when should you use a static class versus a non-static class. In some languages, this distinction doesn’t explicitly exist, but in others, it can impact resource usage and functionality. The matter gets even further convoluted since static classes can bleed into standard procedural programming in some languages.
This article assumes you have some theory with object-oriented programming (OOP) and its implementation in at least one language. The abstract theory is easy enough, but like most things in programming, the devil is in the details. Some languages are object-oriented, and some aren’t (though they’re rare these days), but that doesn’t mean objects and classes are the same across the board. How does your language of choice differ from the ideals behind OOP and where do the visions and ideals of the creator(s) impact this implementation?
Classes
Classes are the abstraction of objects, while objects are the implementation of classes in OOP. This distinction is subtle, but important. Each language which supports object-oriented programming is going to have its own method of actually implementing OOP into classes and objects. Depending on the principles behind the theory or the implementation, you can end up with two completely different ideas with the same base.
Objective C and C++ are built on the same base language of C. C is powerful and flexible, but it predates (mass adoption of) object-oriented programming as a requisite paradigm. The language includes certain elements of object-orientedness, but it isn’t an object-oriented language. The elements are mostly there, but nothing really refines them or binds them into a class system. Objective C and C++ build off of C and implement their own, different class implementations, but they both started with the aim to scratch roughly the same itch.
One of the most notable differences between Objective C and C++ would be multiple inheritance. The one thing they don’t disagree on is the implementation of a difference between static and non-static classes. Languages like Lua have a class system, but it’s more a result of natural features in the language rather than a planned construct. Lua arguably enables object-oriented programming, but doesn’t necessarily facilitate it. It’s doing the legal equivalent of decriminalization for this language feature.
Non-Static Classes
Non-static classes are the classes most people think of when they think of object-oriented programming. Non-static classes incorporate almost every OOP principle by design. You get your encapsulation of data and functions, and you also get your abstraction. The concepts of inheritance and polymorphism can remain a little fuzzy though, but are usually featured in the core of the language.
Lua can add a decimal and an int using the same operator because it treats each of these variables as an object internally. This ends up infringing on typing to a degree. Where does the breakdown between a conscious decision design differ from a very dynamic compiler?
Non-static classes are defined by their dynamic data. They can accept value changes and each object is independent of the other. An object of “dog1” has a different definition than “dog2” though they originate from the same class. This gets weird when an int is an implied object through certain paradigms, while a functional (in the sense of “working”) class is a bit more complex to implement. How is an int with an implied to string function different than a class where you can do the same?
Languages like Lua don’t feel like they’re truly object-oriented in certain respects, but their primitives function more as objects than some class implementations. Classes aren’t necessarily native, but they can be implemented. This theoretical difference leads to a language which is largely dynamic, with some degree of classes, but without a true distinction between non-static and static classes. The developer decides works out static and what doesn’t through their design choices.
Static Classes
How primitives and classes are implemented determines whether there’s a true different between non-static and static classes. Some languages define these by how the class or object is used, while others make it more explicit. This line is blurry in most languages, but the theoretical difference can be relatively profound in how it’s implemented.
A static class basically boils down to a class which can only have a single object implement it. Languages derived from concepts of C++ (Java, C#, etc.) will use a keyword like static to state that a class is static. Variables should remain static and unchanging between uses of the class.
For certain things, this makes perfect sense. A geometry class isn’t going to need to have multiple objects (barring something which abstracts out dimensions or similar). Pi is always going to be the same for any real world application. This naturally begets the question of: if the code isn’t going to change, why should it even be a class?
Different languages’ different implementations mean the answer to this question is going to be highly variable. A static class in Perl is going to be defined by not being non-static rather than being explicitly static. The language built OOP out of encapsulating existing functions and variables rather than shaping the whole specification around a class system. Lua turns metatables into inheritable classes but has its own limitations. C++ and other more statically typed languages tend to make these more obviously split.
The Big Difference
Each class type has its own use case and its own benefits. A static class means you reuse the same elements without needing to deal with instantiating the same things over and over. Non-static classes allow for data to be different but the operations to be the same. The big difference between these boils down to resource usage in a lot of languages and flexibility of the object, as well as the idea behind the approach.
A static class approaches classes with the idea of encapsulating the more reusable parts of a set of operations while allowing flexibility of the functions. Are you targeting the operations or the operations on the data? Static classes care about the operations by themselves without worrying about the data. For highly variable or more one off use cases, this makes more sense and reduces defining pi 100 times in your various circle classes.
Non-static classes care more about operating on their given data. A circle is going to have a different way to calculate the area than a square. Do you want to be responsible for supplying the shape in the beginning or defining each variable when you work with a given shape? You can either have something like geometry.getCircleArea(radius) or playerHitBox.getArea(), or you can get geometry.getTrapezoidArea(top, bottom, vertheight) where you need to calculate the vertical height, or mytrapezoid.getArea(). The choice then becomes, where do you want the specific calculations and data to live, and does it have to be together?
Determining the Difference
The answer to this question impacts what makes the most sense. If you’re working with a multi-tenant environment with a shared database, you arguably get more efficiency implementing a static class. When you need to connect to 100 different environments though, a non-static class is much better, though it might use more memory. If you need to do many operations with a specific client, is it more maintainable to write the code around supplying multiple variables to each function or just instantiating an object once?
The answer to this question really determines which of the approaches is best. While the examples make it clear which is going to work best, in real life, you rarely have this clear of a split. For a circle which is a hit box, does it make more sense for the shape to be independent as its own object or tied into some other structure? For 5 tenants and no plans to grow with a heavily customized database, does it make more sense to make these static or non-static? Ultimately, there isn’t going to be a truly right answer; the answer is determined by what makes the process most efficient to develop and to maintain.
For fuzzier class systems where the schism between static and non-static boils down to implementation, it can really vary depending on what operations are used. Many fuzzier systems end up with this distinction turning into glorified procedural code versus an object system. The difference is that between an ideal partner and an ideal spouse. Hopefully, the only difference is the one on paper, but, the paper does affect how certain things end up working out in certain scenarios.
A Mixed Approach
Most languages can have static elements in their non-static classes in some form (constants and similar constructs), but languages like C# can take a mixed approach to mixing static and non-static elements into the same class. A non-static class can include static elements (though a static class obviously can’t have non-static elements without some nastiness). While this approach can make a lot of sense for certain code, it can also convolute the code worse than a first year CS student who just learned about templates.
While a mixed approach can make certain tasks easier, it can be tempted to use and abuse these sorts of features without understanding where they make sense. For a REST API, a mixed approach can make sense. In a hierarchical, multi-tenant scenario, you may have functions which don’t require special authentication which are useful or required. It can make more sense to set these functions as static to allow other bits of code to do what they need without fully instantiating a new object and without having to move the code somewhere else.
The use case is relatively rare, but it has helped me with certain API versioning, API specific conversions, and JSON parsing functions which didn’t make sense to fully split out. It can be powerful, but it can also make the project a bit harder to understand if not done right. You need to understand what the purpose of the code is and how it will be used to know if this is a correct approach or not.
Procedural Libraries Vs. Static Classes
What is the difference between a block of procedural code and a static class? This line can be extremely blurry in many languages, but I always interpret it as whether the code is trying to live up to object-oriented principles. Where does procedural code which abstracts, encapsulates, and is polymorphic turn into a static class?
There really isn’t a hard delineation for many languages. A basic OOP aware, static class in Perl can be indistinguishable from a procedural library. The difference lies in how the language is employed. Is your code written with OOP in mind or is it just a byproduct?
This is the major difference which separates procedural libraries from static classes. Some languages give you the tools to really differentiate the code and enforce a use case while others don’t. You can’t help but write object-oriented code in some languages. Is a struct with function pointers in C an object or a mess? Where do we cross into an object-oriented paradigm for our code?
Which Is Best?
Ultimately, it doesn’t matter what you use as long as you use a given tool as it’s intended to be used. Don’t write stereotypical Java or C++ code shoehorning in templates and interfaces for the sake of using them. Write code which solves a problem and solves it well. A circle class doesn’t need a template, nor does it usually make sense to derive inheritance from an arbitrary shape class. The code just becomes convoluted for the sake of enforcing principles which go against pragmaticism.
Static classes have their use cases, but are absolutely useless for certain use cases in object-oriented languages. Don’t try to force them one way or the other. Non-static classes are going to exist with any class system, so where does it make sense to limit the class? Consider why you would prefer one over the other and how it makes your project more understandable and more scalable.
Are you writing code where there are core bits of data which never change or are you working with highly variable data? Is it easier to encapsulate the individual data in a class or pass the data to a method in a static class? The difference between the two boils down to preference for the more questionable use cases.
What are you using the code for and what makes it efficient and maintainable? We’re past trying to eke out every byte of RAM (for most things). Use what makes your job both building and supporting the project easier and more efficient. The best choice is the best fit for scalability, readability, and reusability, but what determines that will depend on the exact implementation and usage. Consider the why as much as the how when you implement a given class and you’ll quickly see where static classes excel and where they fall short.
Image by Borko Manigoda from Pixabay