适配器模式
本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 51 课,共 65 课。
适配器模式可以让对象与需要不同方法的代码协同工作。适配器会包装对象,并将每个预期的调用转换为对象能够理解的调用。
早期项目中的一个旧类用两个数字存储生命值:
# old_monster.gd
class_name OldMonster
extends RefCounted
var life := 30
var max_life := 120你的 HUD 会显示任何具有 get_label() 和 get_hp_percent() 的对象。Knight 已经有这两个方法(它会返回 "Knight" 和 80),但 OldMonster 两者都没有:
# health_display.gd
class_name HealthDisplay
extends RefCounted
func show_health(target) -> void:
print(target.get_label(), ": ", target.get_hp_percent(), "%")适配器持有一个 OldMonster,并提供显示器调用的两个方法:
# monster_adapter.gd
class_name MonsterAdapter
extends RefCounted
var monster: OldMonster
func _init(wrapped: OldMonster) -> void:
monster = wrapped
func get_label() -> String:
return "Monster"
func get_hp_percent() -> int:
return monster.life * 100 / monster.max_life# main.gd
var display := HealthDisplay.new()
display.show_health(Knight.new())
display.show_health(MonsterAdapter.new(OldMonster.new()))Knight: 80%
Monster: 25%OldMonster 和 HealthDisplay 都没有改变。这一点很重要,因为有时你无法或不应该编辑一个类:它来自插件,由另一个团队负责,或者有许多脚本依赖它的名称。
适配器会转发每次调用,并在过程中转换数据,这里是把两个数字转换成一个百分比。先乘后除:使用整数时,30 / 120 * 100 的结果是 0,而 30 * 100 / 120 的结果是 25。
挑战
简单天气面板会显示具有 get_sensor_name() 和 get_celsius() 的传感器,例如 ModernSensor(modern_sensor.gd)。面板位于 weather_board.gd 中。旧式温度计(legacy_thermometer.gd)只提供 read_fahrenheit() 和 get_code()。编写适配器 ThermometerAdapter:
- 构造函数会存储被包装的
LegacyThermometer。 get_sensor_name()返回Thermometer、一个空格和温度计的代码,例如Thermometer TH-4。get_celsius()以float类型返回摄氏温度:(fahrenheit - 32) * 5 / 9。
不要更改其他类。提供的代码会读取后续传感器的数量,然后逐行读取每个传感器的信息:legacy ID FAHRENHEIT 或 modern NAME CELSIUS。它会用你的适配器包装每个旧式温度计,并显示所有传感器。对于 legacy 4 98.6 和 modern Garden 21.5,输出为:
Thermometer TH-4: 37.0 C
Garden: 21.5 C
Warmest: Thermometer TH-4你的代码写在 thermometer_adapter.gd 中。main.gd 包含提供的输入/输出代码,不能编辑。
亲手试试
extends Node
func _ready():
# 提供的输入输出代码:保持原样。
var board := WeatherBoard.new()
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "legacy":
var old := LegacyThermometer.new(int(parts[1]), float(parts[2]))
board.add_sensor(ThermometerAdapter.new(old))
else:
board.add_sensor(ModernSensor.new(parts[1], float(parts[2])))
board.show_all()
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: GDScript 在线编译器