Abstract classes in 4.5

Question

I noticed that ‘abstract classes’ are going to be introduced in Godot 4.5
I’m not a programmer. I tried to understand why I would need an abstract class. But I had an impression that class inheritence works like abstract class, so I don’t understand the necessity of a seperate abstract class.
But I’m pretty sure there shoul be more to it, otherwise what is the point, right? :slight_smile: So I was hoping someone can explain or point me to the right direction.

2 Likes

As I understand it, an abstract class is exactly the same as a normal class except it can’t be instantiated. It’s used as a “template” to be inherited by other classes. For example:

You have three different types of guards, SwordGuard, SpearGuard, and FirearmGuard. They all have different stats but because the share the same behaviour, they should inherit a base class Guard that stores all shared information. The stats have to differ, which you can accomplish by declaring them in Guard and setting them in the _init() method of each individual guards:

# e.g. sword_guard.gd

func _init() -> void:
	damage_output = 0.5
	attack_speed = 4

Before 4.5, this approach can lead to mistakes because, even though Guard isn’t a valid class on its own, it can be instantiated. Abstract classes solve this problem by allowing programmers to prevent incomplete classes from being instantiated.

8 Likes

I’ll try to add my take on this - also since you said you are not a programmer.
As @yesko explained an Abstract class is nothing more than a class that can’t be instantiated. It is heavily used in inheritance-focued OOP. However - at least in my experience - Godot doesn’t really push for an inheritance-focused approach, rather a composition-based one.

If you don’t know about composition please refer to this nice video by Bitlytic.

So how does abstraction come in handy in a composition-based approach? Since you don’t really describe an entity as a set of variable in a class, but rather a collection of nodes in the hierarchy, it may seem useless, right?

I already use a similar approach - which is just a naming criteria that I apply in my daily programming - I simply name all my abstract classes along the lines of AbstractMyClass, and then inherit from there.

But instead of creating an abstract class for a generic Entity, I use this approach for a single component that - for a reason or for another - needs to have different behaviours.

As for the why abstract classes are useful and enforce the fact that they can’t be instantiated, well it’s simply to put some logical restrictions/rules into place.

To give you an easy example - focussing on inheritance and fogetting for a moment the concept of composition - If you have a Player and an Enemy class, they are likely to be both inheriting from a base Entity class which holds fields such as health, damage, movement_speed, etc. Something like this:

class_name Player
extends AbstractEntity

# All the following is Player specific (Enemy does not have this)

func some_player_specific_action() -> void:
     pass
class_name Enemy
extends AbstractEntity

# All the following is Enemy specific (Player does not have this)

func some_enemy_specific_action() -> void:
     pass
abstract class_name AbstractEntity
extends Node

# All the following will be present in both Player and Enemy

@export var health: float = 100.0
@export var damage: float = 10.0
@export var movement_speed: float = 5.0

func take_damage(amout: float) -> void:
     # just an example here
     self.health -= amount

func some_generic_entity_action() -> void:
     pass

Now, of course you want to have an instance of Player and one of Enemy, at it would make sense. But would it make sense to - even by accident - have an instance of an Entity that is a non-defined something? Of course not! And that’s where abstraction comes into play. That way you can simply avoid any unwanted instantiation.

Not to mention the fact that by leveraging inheritance you can also leverage polymorphism also to refer at both entity indistinctively. So for instance, if you want to have an object (a trap) damage an entity, you don’t need to know if it’s a Player or an Enemy. You simply know it’s an AbstractEntity, and that AbstractEntity has a function take_damage, so you leverage it to apply damage regardless of the actual instance you collide with.

I am not aware if abstraction will also be applicable to functions (I hope so), but using other languages other than GDScript, you can also apply abstraction to methods to enforce overriding them and make sure that must-haves are implemented for every child class.

5 Likes

You’ll be happy to hear that yes, we’re getting both abstract classes and methods :slight_smile:

4 Likes

Good morning. I just wanted to respond to your question with my own experience from my previous undergraduate program. To begin, I would like to start by agreeing with everything everyone else here in this thread has posted and commented regarding the nature of abstraction via abstract classes, with respect to how they enable and allow for polymorphism and, thus, encapsulation of your code. Throughout my previous undergraduate program, my university required us to use Java as our primary programming language for all four years of our undergraduate education to instruct us on fundamental core concepts (e.g. Data Structures, Algorithms, Parallel Programming, etc.). One of the classes taken was on Software Construction, in which the abstract nature of interfaces in Java (which are basically just a list of abstract classes we implement in a class in Java) is fundamental. Basically, think of abstract classes as much like a contract, in which the list of abstract methods you want to implement in a program are required to be included in said program/script, regardless of whether or not they return any value, perform any method, or are every called or executed. Think of it as, when you define an abstract class that takes (or does not take) a particular value or list of values, and returns (or does not return) a value, you are saying “the script of the node for this object or program must include this method within it for any reason”. This allows you as the programmer to make use of Software Design Patterns (e.g. Factory Pattern, Adapter Pattern, Decorator Pattern, etc.) throughout your work to simplify software source code implementation, thereby making it much more scalable and, consequently, easier to maintain in the future. Of course, I have noticed the node-based nature of Godot to be far different than my experience in scripting in Java from my undergrad, so, I would recommend referring to GDQuest’s text-based tutorials on Software Design Patterns (Design patterns in Godot · GDQuest), as well as the online PDF noted in the Godot Docs for game programming patterns (https://gameprogrammingpatterns.com/), as the Python-like nature of GDScript, as well as the separation in scripts across various nodes may render the implementation and design of Software Design Patterns distinctly different from that of Java. Wishing you the best of luck in your programming and video game development endeavors!

3 Likes

You don’t.


Other replies have done a good job of describing them, and if you’re interested in learning to be a programmer, you want to get to an understanding of them eventually. @colelli 's example of an abstract class is a good one in games.

I specifically use a base Character class that my Player, Enemy and NPC classes all inherit from. I will probably make this Character class Abstract in the future. However to your other question:

I don’t actually have to make Character an abstract class. It will still work. The benefit of using an Abstract class is communication. When you declare something as Abstract, you state your intention to future users of that class that this class is not intended to be used on its own - it is meant to only be used by classes that derive from it. That future user is most likely going to be you.

TBH, going back to my initial response, you don’t want to use it until you’ve watched the videos and read the links that others have already posted. Things like Abstraction are very useful, very powerful tools. And I see new programmers overengineer things all the time because they are seduced by wanting their code to look professional or smart.

If you have a whiteboard in your bedroom (like I do) and plan out all your code in UML diagrams before coding, then Abstraction will probably be useful to you. If you are creating code for a Godot plugin, or some sort of template that is intended for use by people that are not you - Abstraction may be useful to you. If you refactor your code because you are now realizing that some classes you created should be abstract, Abstraction will be useful to you.

It will also be useful to you when you create a bunch of abstractions and then realize a few months down the road that you’ve made your code overly elegant and it’s hard to maintain and take it back out. That’s when you will really learn about abstraction.

Mostly though, in my opinion, it’s not something you should spend too much time worrying about.

4 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.