Menu
CoddyTech

Проектирование интерфейсов

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

Интерфейс — это список методов, которые класс обещает иметь, но без реализации. В Java и C# есть ключевое слово interface; в GDScript его нет. То же самое можно создать с помощью абстрактного класса, все методы которого являются абстрактными:

# drawable.gd
@abstract
class_name Drawable
extends RefCounted

@abstract func draw() -> String
@abstract func resize(factor: int) -> void

В нём нет переменных и тел методов. Он описывает, что может делать рисуемый объект, а каждый класс сам решает, как именно:

# banner.gd
class_name Banner
extends Drawable

var text: String
var size := 1

func _init(t: String) -> void:
	text = t

func draw() -> String:
	return "Banner '%s' at size %d" % [text, size]

func resize(factor: int) -> void:
	size *= factor

Circle тоже может расширять Drawable, иметь radius и собственные методы draw() и resize(). Код, использующий интерфейс, зависит только от него, поэтому он работает с каждым классом, который расширяет Drawable, включая классы, написанные позднее:

func render_all(items: Array[Drawable]) -> void:
	for item in items:
		item.resize(2)
		print(item.draw())
var items: Array[Drawable] = [Banner.new("Sale"), Circle.new(3)]
render_all(items)
Banner 'Sale' at size 2
Circle with radius 6

У класса есть один родитель, поэтому он может расширять только один интерфейс. Когда класс выполняет вторую роль, например сохраняется в файл, расширь основной интерфейс и проверь вторую роль с помощью has_method():

if item.has_method("save"):
	item.save()

Делайте каждый интерфейс небольшим: несколько методов, относящихся к одной роли. Класс, который его расширяет, затем реализует только необходимые ему методы.

challenge icon

Задание

Легко

Создай медиаплеер на основе интерфейса.

  • В playable.gd сделай Playable интерфейсом: абстрактным классом с абстрактными методами play() -> String и duration() -> int (в секундах).
  • Song наследуется от Playable. Метод play() возвращает Playing song: TITLE by ARTIST, а duration() возвращает длительность в секундах.
  • Video наследуется от Playable. Метод play() возвращает Playing video: TITLE in RESOLUTIONp (например, in 1080p), а duration() возвращает длительность в секундах.
  • Playlist знает только о Playable. Метод play_all() выводит результат play() каждого элемента, а total_time() возвращает суммарную длительность в формате M:SS (секунды записываются двумя цифрами, например, 4:05).

Конструкторы Song и Video уже написаны. Предоставленный код считывает количество следующих элементов, а затем по одной строке на каждый элемент: song TITLE ARTIST SECONDS или video TITLE RESOLUTION SECONDS. Он вызывает play_all() и выводит общее время. Для song Imagine Lennon 183 и video Trailer 1080 95 он выводит:

Playing song: Imagine by Lennon
Playing video: Trailer in 1080p
Total time: 4:38

Твой код нужно написать в playable.gd, song.gd, video.gd и playlist.gd. В main.gd находится предоставленный код ввода и вывода, его нельзя редактировать.

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

extends Node

func _ready():
	# Предоставленный код ввода-вывода: оставьте его без изменений
	var count := int(OS.read_string_from_stdin())
	var playlist := Playlist.new()
	for i in count:
		var p := OS.read_string_from_stdin().split(" ")
		if p[0] == "song":
			playlist.add(Song.new(p[1], p[2], int(p[3])))
		else:
			playlist.add(Video.new(p[1], int(p[2]), int(p[3])))
	playlist.play_all()
	print("Total time: " + playlist.total_time())
quiz iconПроверьте себя

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

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

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