<!-- 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.
