And I OOP: Object-Oriented Techniques and the Struggle to Understand Them
Bit of background on me, I’m currently in my final year of a programming-based degree. It has a more specialised focus on using what is taught and applying it to game engines & programming, but at its core is teaching good practice and technique within programming languages.
The majority of these languages are object-oriented (OO). Object-oriented languages are (surprising no one) built on the concept of pooling code and data into “objects”. These two core components of an object can be defined within a “class”; a class is essentially a blank state of an object and once created, can be used to create instances of these objects.
OOP languages put a heavy focus on making code modular and reusable. This is often achieved by looking at a piece of code and thinking “how often am I going to need to use this?”. If it’s used for one start-up operation, it can just be left be.
If it’s something that is core to the main operation of the program; its attributes can be placed into a class and the code is then placed into a “function”, meaning now that whenever that piece of code is needed, the programmer can just call the function as opposed to writing/copying the code out again.
All languages are going to have different implementations of various features, but there’s 3 features which are implemented across the majority of them:
I’m going to explain what each of those mean in detail, along with talking about my struggles with actually trying to implement them in my code. All examples I give are going to be in C#/C++ as this is the syntax I feel most comfortable with.
When talking about data stored in a class, the phrase “visibility” will be used a lot. Visibility refers to what variables/methods the main program can see/access, in C#/++ this is mainly defined in three levels:
- Public: the variable/method is visible to all parts of the program once an instance of the class is created
- Private: the variable/method is only accessible within the class
- Protected: as above, but can also be access in derived classes (more on that later)
By default when a variable is declared, its access modifier is set to private. This is also good practice, as it means you don’t expose important data of a class to other parts of the program. But what if you need to use or modify this data?
This is where the implementation of “getters” and “setters” come into play. A “getter” is a function call that simply returns the value of the variable, whereas a “setter” is a function call that modifies the value of the variable, either through passing in a value to add/subtract or providing a parameter to plug into an algorithm.
In C# this is achieved through the use of creating a private variable and a public property. The variable stores the value, whereas the property acts as both getter and setter (and when programming, some IDEs will give you the option to create a property as you declare a variable).
This is a bit more complex in C++, but not by much. Properties aren’t able to be created by default, so you either need to make your variables public which is not the best idea or, as is standard, write individual getter/setter functions into your classes. Whilst this can feel clunky, it also allows for more efficient design, if you have a variable that only needs to be passed to other classes; all it requires is a getter to be written.
Out of all 4 concepts, this is the one I have the best grasp of. When learning C++ and knowing I had to write my own function calls for passing variables, I became more aware of how encapsulation can shape the design of a class and influence how functions that require data from other classes are designed.
If you ever want to make a sustainable income in programming games, you want to have one as a backup.
…that’s not what this means? Huh. (Game devs deserve so much better)
Inheritance continues the idea of writing reusable code, as it allows you to create a base class that can then be inherited from to create “derived” classes. Derived classes are able to access public member functions and protected variables of parent classes. The way inheritance can be shown is through a “class diagram”, showing the tree and/or lattice of classes.
The example that is commonly used to explain this is by showing the base class of “shape”, which will contain basic data on what a shape is defined as.
This base class is then used as the parent class of two derived classes, “2D shape” and “3D shape”, both of which extend the base class by adding in data such as “area” and “volume” respectively. These two derived classes can then be used as parent classes for defining derived classes for specific shapes, cutting down on needing to re-write variables for each new class.
Whilst I am still unsure on this concept, I’ve slowly learnt how it can be used effectively. When applying it to games programming, it makes planning out non player characters (NPCs) much simpler, as you can create a base class for basic NPC function and then create derived classes to make diverse NPCs with less new code needing to be added.
Polymorphism is… strange, at least from my perspective.
It is often defined as “the ability for a function, variable or object to take on different forms”, with this definition being applied to allow programmers to design their software without needing to think of specifics.
In C#/++, this is seen as declaring a function with the same name multiple times, but changing what input parameters it has:
- Output(int number)
- Output(int number1, int number 2, int number 3)
- Output(float number)
- Output(vector<int> vector) [an aside, I love vectors in C++, such a useful data structure]
These functions all have the same name of “Output”, but the actual nature of Output will only be determined when they are called with the specific set of parameters.
I’ve not yet been able to see a useful application of polymorphism in my programs; I did briefly flirt with the idea of trying to write a function with an infinitely scalable number of parameters, but I got quickly talked out of it. From the perspective of games programming, it can potentially tie into inheritance, a function called “Movement” can be defined in a base NPC with it’s true behaviour only being determined when applied to specific types of NPCs.
Without sounding too full of myself, I think I’m a pretty good programmer. My problem solving skills are usually pretty sharp and (aside from a few flukes) I’m able to effectively line-by-line debug any errors I may have.
These techniques, and being able to use them correctly, comes over time. I know for a fact that even when I’m starting to do this sort of work with the aim of producing my own games, I’ll be learning more about how I may be able to implement them; maybe in ways I never considered I could.