Menu
CoddyTech

Паттерн «Шаблонный метод»

Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 53 из 65.

Шаблонный метод фиксирует порядок шагов алгоритма в родительском классе и позволяет каждому подклассу выполнять некоторые из этих шагов. Родительский метод, который выполняет шаги, называется шаблонным методом.

# 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() — это шаблон: каждый уровень загружается, создаёт своих врагов и завершается именно в таком порядке. Отличающиеся шаги являются абстрактными, поэтому каждый подкласс должен реализовать их:

# forest_level.gd
class_name ForestLevel
extends Level

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

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

CaveLevel записан так же, с названием "Cave" и появляются 5 летучих мышей.

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

У шага также может быть значение по умолчанию в родительском классе, которое подкласс может переопределить. Такой необязательный шаг называется хуком:

# 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

Только уровень, переопределяющий has_boss() так, чтобы он возвращал true, получает строку с боссом. Подклассы задают шаги, а родительский класс решает, когда они выполняются, поэтому подкласс не изменяет play(), и все уровни следуют одному и тому же порядку.

challenge icon

Задание

Средне

Игра экспортирует результаты в двух форматах, используя одни и те же шаги. Строки — это словари с "name" и "score".

  • В exporter.gd напиши шаблонный метод export_rows(rows): он выводит header(), затем format_row(row) для каждой строки, а затем footer(rows). Пустой заголовок или подвал не выводится. Методы-обработчики header() и footer() по умолчанию возвращают "", а метод format_row() является абстрактным.
  • CsvExporter: заголовок — name,score, строка — Ada,12. Без подвала.
  • ListExporter: без заголовка, строка — * Ada scored 12, а подвал — Players: N, где N — количество строк.

Предоставленный код считывает формат (csv или list), количество следующих строк, а затем по одной строке NAME SCORE в каждой строке. Для list с Ada 12 и Bo 7 результат будет таким:

* Ada scored 12
* Bo scored 7
Players: 2

Твой код размести в exporter.gd, csv_exporter.gd и list_exporter.gd. В main.gd находится предоставленный код ввода-вывода, его нельзя редактировать.

Попробуйте сами

extends Node

func _ready():
	# Предоставленный код ввода-вывода: оставьте его без изменений
	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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор GDScript