<!-- Generated from applied-computing-au vic/unit3-4/sat/C06-2026/C06-Reference-Godot by port-reference-godot-to-wiki.py — do not hand-edit; re-run the port. -->
# OOP Concepts — GDScript


**Skill codes on this page**

| Code | Level | Skill |
|---|---|---|
| C643 | 7–8 | access modifiers |
| C651 | 9–10 | classes |
| C652 | 9–10 | objects |
| C653 | 9–10 | abstraction |
| C654 | 9–10 | encapsulation |
| C655 | 9–10 | generalisation |
| C656 | 9–10 | inheritance |

**Definition.** *Object-oriented programming (OOP):* programming based on objects that contain data in the form of fields or attributes, and code in the form of methods.

Level 9–10 asks for classes and objects **plus all relevant OOP principles** — one class on its own does not reach the top band.

## C651 — Classes and C652 — Objects

**Definitions.** *Class:* a program code template for creating objects. *Object:* any instantiated class that a program can inspect and/or change. *Instantiation:* the process by which an object is created from a class.

```gdscript
# MykiCard.gd
class_name MykiCard        # C651 — class: the template for every card
extends RefCounted

var _balance: float = 20.00

func top_up(amount: float) -> void:
    _balance += amount
```

```gdscript
# C652 — objects: two cards from one template, separate balances
var my_card: MykiCard = MykiCard.new()
var your_card: MykiCard = MykiCard.new()
my_card.top_up(10.0)
```

**Earns the tick:** C651 — a class *you wrote* (`class_name`, fields, methods). C652 — `.new()` creating an object your program then uses. Two objects holding different state is the cleanest demo.

**In tile-matching-game:** `card.gd` opens with `class_name Card` — fields (`pair_id`, `is_face_up`) plus methods (`flip_up()`, `mark_matched()`) in one template (C651). The game then holds two of them:

```gdscript
# tile-matching-game — game.gd
var first_card: Card = null    # C652 — two objects from one class,
var second_card: Card = null   # each with its own state
```

(The board creates them with `card_scene.instantiate()` — instancing the card *scene* constructs a `Card` object, because the scene's root node runs `card.gd`.)

**In club-ladder** — the direct `.new()` case:

```gdscript
# club-ladder — app.gd
season.teams.append(Team.new(team_name))   # C652 — object created and used
```

`season.gd` builds them four arguments at a time from the XML — `Team.new(team_name, wins, draws, losses)` — and `Team._init` gives every parameter a default, so both calls work.

## C653 — Abstraction

**Definition.** An OOP principle that allows programmers to manage complexity by hiding implementation details and exposing only the essential features of an object.

**How it works in GDScript:**

- Base class defines structure meant for inheritance
- Methods can have default implementations or error messages
- Convention-based — not meant to be used directly
- `assert(false)` is one way to indicate override needed
- Focus on inheritance structure rather than enforcement

```gdscript
# C653 — abstraction: base class with common structure
class_name Animal
extends RefCounted

var name: String

func _init(p_name: String):
    name = p_name

func make_sound():
    print("Some animal sound")

func move():
    print(name + " is moving")
```

Myki version:

```gdscript
# C653 — abstraction: callers use get_cost() and never see the fare rules
class_name Journey
extends RefCounted

var fare: float

func get_cost() -> float:
    return fare
```

**Earns the tick:** a base class used through its simple interface while the details stay inside. "The HUD calls `get_cost()` and doesn't care how the cost is worked out" is the sentence to say.

**In tile-matching-game:** `card.gd`'s own header comment makes the abstraction argument — a card "does NOT decide whether two cards match — that is the Game's job. The card only reports being clicked." The game calls `flip_up()` and `mark_matched()`; how a card draws itself stays hidden inside the card.

## C654 — Encapsulation

**Definition.** An OOP principle that involves bundling the data, and the methods that operate on that data, into a single unit or class.

**How it works in GDScript:**

- Single underscore `_` indicates private by convention only
- No enforcement mechanism — purely documentation
- All class members are technically public
- Relies entirely on developer discipline
- IDE may highlight convention violations

```gdscript
class_name BankAccount
extends RefCounted

var _balance: float = 0.0  # C654 — private by convention

func get_balance() -> float:
    return _balance

func deposit(amount: float):
    if amount > 0:
        _balance += amount
        print("Deposited $" + str(amount))
    else:
        print("Invalid amount")
```

Myki version:

```gdscript
class_name MykiCard
extends RefCounted

var _balance: float = 20.00       # C654 — private, cannot be changed directly

func _is_valid(amount: float) -> bool:   # C654 — private helper, used only inside this class
    return amount > 0

func top_up(amount: float) -> void:      # C654 — the only door to _balance
    if _is_valid(amount):
        _balance += amount
```

**Earns the tick:** data behind `_`, changed only through methods that validate. In validation: show `top_up(-50)` being rejected.

**In tile-matching-game:** `Card` bundles its state (`pair_id`, `is_face_up`, `is_matched`) with the methods that change it — outside code never *sets* those fields, it calls `flip_up()`, `flip_down()`, `mark_matched()`. (The game reads them directly; underscore-prefixing the fields and adding getters would push this to full-marks encapsulation.)

### C643 — Access modifiers

GDScript has no `private` / `public` keywords — the `_` prefix **is** the access modifier, applied by convention:

```gdscript
var _balance: float = 20.00    # C643 — "private": underscore convention
func get_balance() -> float:   # C643 — public getter is the only way in
    return _balance
```

**Earns the tick:** the convention applied consistently *and named* — "the underscore marks it private by convention; other code must go through the getter."

**In connect-go-dots:** `_open()` and `_on_back_pressed()` carry the underscore as internal helpers no other script should call. (`_ready()` also starts with `_`, but that is Godot's callback naming, not privacy.)

**In tile-matching-game:** `Card` shows the split in one class — public `flip_up()` / `mark_matched()` for other scripts, private `_on_pressed()` for itself.

## C655 — Generalisation

**Definition.** The process of defining a general class (superclass) that encapsulates common attributes and behaviours of more specific classes (subclasses).

**How it works in GDScript:**

- `class_name` makes class globally accessible
- `extends RefCounted` for automatic memory management
- Constructor `_init` sets up shared data
- Base class defines common interface for all subclasses

```gdscript
# C655 — generalisation: common functionality in base class
class_name Vehicle
extends RefCounted

var brand: String
var model: String
var is_running: bool = false

func _init(p_brand: String, p_model: String):
    brand = p_brand
    model = p_model

func start_engine():
    is_running = true
    print(brand + " " + model + " started")

func stop_engine():
    is_running = false
    print(brand + " stopped")
```

Myki version:

```gdscript
# C655 — generalisation: what every journey shares lives in the parent
class_name Journey
extends RefCounted

var zone: int
var fare: float

func _init(p_zone: int, p_fare: float):
    zone = p_zone
    fare = p_fare

func get_cost() -> float:
    return fare
```

**Earns the tick:** shared fields and methods pulled *up* into one parent instead of repeated in each child — point at the duplication you avoided.

## C656 — Inheritance

**Definition.** A method of basing an object or class on another object or class, taking on its attributes and methods and potentially extending upon them.

**How it works in GDScript:**

- `extends` establishes parent-child relationship
- `super()` calls parent constructor with parameters
- Override by redefining method with same name
- Single inheritance model (one parent)
- Parent methods available unless explicitly overridden

```gdscript
# C656 — inheritance: Car is a Vehicle with extras
class_name Car
extends Vehicle

var doors: int

func _init(p_brand: String, p_model: String, p_doors: int):
    super(p_brand, p_model)
    doors = p_doors

func honk():
    print("Beep beep!")

# C656 — override parent method
func start_engine():
    super.start_engine()
    print("Car is ready to drive")
```

Myki version:

```gdscript
# C656 — inheritance: a ConcessionJourney is a Journey with one changed rule
class_name ConcessionJourney
extends Journey

func get_cost() -> float:   # override
    return fare * 0.5
```

**Earns the tick:** `extends` *your own* class, `super` used, at least one method overridden — and both parent and child actually used by the running program.

**Not enough:** every script in connect-go-dots has `extends Control` or `extends Node` — inheriting from an *engine* class. Necessary in Godot, but C656 wants `extends` on a class of your own.

**Worth seeing anyway:** in tile-matching-game, `Card extends Button` shows what inheriting *buys* — the card never defines `pressed` or `disabled`, yet uses both, because they come from `Button`. That is inheritance working; it just is not *your* parent class, so on its own it does not earn C656.

## Check Your Understanding

1. Why does `extends Control` not earn C656?

>| ### Answer
>| It inherits from an *engine* class — the tick needs `extends` on a class you wrote.

2. What is GDScript's access modifier, and which code is it?

>| ### Answer
>| The `_` underscore prefix, applied by convention — C643.

3. List the minimum ingredients of full C656 evidence.

>| ### Answer
>| Your own parent class, `extends` on the child, `super` used, at least one override — and both classes used by the running program.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9