Menu
CoddyTech

信息隐藏

本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 28 课,共 65 课。

信息隐藏比私有变量更进一步:使用类的代码只依赖其公有方法的功能,而不依赖该类如何存储数据。这样,你就可以更改存储方式,而无需修改那段代码。

# inventory.gd
class_name Inventory
extends RefCounted

var _counts: Dictionary = {}

func add(item: String, amount: int) -> void:
	_counts[item] = _counts.get(item, 0) + amount

func count(item: String) -> int:
	return _counts.get(item, 0)

func remove(item: String, amount: int) -> bool:
	if count(item) < amount:
		return false
	_counts[item] -= amount
	return true

main.gd 调用 add()、count() 和 remove(),完全不需要接触字典。之后如果 Inventory 改为用数组保存槽位,只需修改 inventory.gd。

要小心返回 Array 或 Dictionary 的 getter。GDScript 会按引用传递这些值:调用方拿到的是对象自己的 Array,而不是副本,并且可以绕过类的方法直接修改它:

# quest_log.gd
var _quests: Array[String] = ["Find the key"]

func get_quests() -> Array[String]:
	return _quests
# main.gd
var journal := QuestLog.new()
var quests := journal.get_quests()
quests.append("Skip the boss")
print(journal.get_quests()) # ["Find the key", "Skip the boss"]

改为返回副本。duplicate() 会创建一个包含相同元素的新数组,因此对它的修改不会影响对象:

func get_quests() -> Array[String]:
	return _quests.duplicate()

对于字典,或者包含其他数组的数组,使用 duplicate(true):它也会复制内部的数组和字典。

challenge icon

挑战

简单

在 playlist.gd 中编写 Playlist 类。它使用私有数组 _songs 保存歌曲标题,其他代码只使用以下方法:

  • add(title) 会追加标题并返回 true。如果标题已在播放列表中,则不做任何更改并返回 false。
  • remove(title) 会移除标题并返回 true;如果标题不在播放列表中,则返回 false。
  • count() 返回歌曲数量。
  • get_titles() 按照歌曲添加的顺序返回标题的副本。

提供的代码会读取命令数量,然后逐行读取命令(add Blue Sky 或 remove Blue Sky)并打印结果。最后,它会获取 get_titles() 返回的数组,追加 Intruder 并将其反转,然后打印歌曲数量和标题。播放列表不能受到这些更改的影响。对于命令 add Blue Sky、add Rain 和 add Blue Sky,程序会打印:

Added: Blue Sky
Added: Rain
Already in the playlist: Blue Sky
Songs: 2
Blue Sky, Rain

你的代码写在 playlist.gd 中。main.gd 包含提供的输入/输出代码,不能修改。

亲手试试

extends Node

func _ready():
	# 提供的输入/输出代码:保持原样
	var playlist := Playlist.new()
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ", true, 1)
		var title := parts[1]
		if parts[0] == "add":
			if playlist.add(title):
				print("Added: " + title)
			else:
				print("Already in the playlist: " + title)
		elif playlist.remove(title):
			print("Removed: " + title)
		else:
			print("Not found: " + title)
	# 尝试修改返回的列表的代码
	var titles := playlist.get_titles()
	titles.append("Intruder")
	titles.reverse()
	print("Songs: %d" % playlist.count())
	print(", ".join(playlist.get_titles()))
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: GDScript 在线编译器