Menu
CoddyTech

Template Method Pattern

Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 53 of 65.

The Template Method pattern fixes the order of an algorithm's steps in a parent class and lets each subclass fill in some of the steps. The parent method that runs the steps is the template method.

# level.gd
@abstract
class_name Level
extends RefCounted

func play() -> void:
	print("Loading ", get_level_name())
	spawn_enemies()
	print("Level complete!")

@abstract func get_level_name() -> String
@abstract func spawn_enemies() -> void

play() is the template: every level loads, spawns its enemies and completes, in this order. The steps that differ are abstract, so every subclass has to write them:

# forest_level.gd
class_name ForestLevel
extends Level

func get_level_name() -> String:
	return "Forest"

func spawn_enemies() -> void:
	print("3 wolves appear")

CaveLevel is written the same way, with the name "Cave" and 5 bats appear.

# main.gd
var levels: Array[Level] = [ForestLevel.new(), CaveLevel.new()]
for level in levels:
	level.play()
Loading Forest
3 wolves appear
Level complete!
Loading Cave
5 bats appear
Level complete!

A step can also have a default in the parent that a subclass may override. Such an optional step is called a hook:

# level.gd
func play() -> void:
	print("Loading ", get_level_name())
	spawn_enemies()
	if has_boss():
		print("The boss arrives")
	print("Level complete!")

func has_boss() -> bool:
	return false

Only a level that overrides has_boss() to return true gets the boss line. The subclasses write steps and the parent decides when they run, so a subclass leaves play() alone and every level follows the same order.

challenge icon

Challenge

Medium

A game exports its scores in two formats with the same steps. Rows are Dictionaries with a "name" and a "score".

  • In exporter.gd, write the template method export_rows(rows): it prints header(), then format_row(row) for every row, then footer(rows). A header or footer that is an empty string is not printed. The hooks header() and footer() return "" by default, and format_row() is abstract.
  • CsvExporter: the header is name,score, and a row is Ada,12. No footer.
  • ListExporter: no header, a row is * Ada scored 12, and the footer is Players: N with the number of rows.

The supplied code reads the format (csv or list), how many rows follow, then one NAME SCORE row per line. For list with Ada 12 and Bo 7 the output is:

* Ada scored 12
* Bo scored 7
Players: 2

Your code goes in exporter.gd, csv_exporter.gd and list_exporter.gd. main.gd holds the supplied input/output code and cannot be edited.

Try it yourself

extends Node

func _ready():
	# Supplied input/output code: keep it as it is
	var style := OS.read_string_from_stdin()
	var rows: Array = []
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		rows.append({"name": parts[0], "score": int(parts[1])})
	var exporter: Exporter = CsvExporter.new()
	if style == "list":
		exporter = ListExporter.new()
	exporter.export_rows(rows)
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online GDScript compiler