Menu
CoddyTech

Định kiểu vịt

Thuộc phần Lập trình hướng đối tượng trong hành trình GDScript của Coddy. Bài học 22 trên 65.

Duck typing tập trung vào những gì một đối tượng có thể làm, chứ không phải nó thuộc lớp nào. Nếu đối tượng có phương thức bạn gọi, lệnh gọi đó sẽ hoạt động. Tên gọi này bắt nguồn từ câu nói “nếu nó bơi như vịt và kêu như vịt thì nó là vịt”.

Hai lớp này không có lớp cha chung, nhưng cả hai đều có swim() và quack():

# duck.gd
class_name Duck
extends RefCounted

func swim() -> String:
	return "Duck swimming"

func quack() -> String:
	return "Quack!"
# robot.gd
class_name Robot
extends RefCounted

func swim() -> String:
	return "Robot swimming with propellers"

func quack() -> String:
	return "Robot sound: BEEP BEEP!"

Tham số không có kiểu dữ liệu sẽ chấp nhận mọi đối tượng. Hàm gọi các phương thức, và mỗi đối tượng tự chạy phương thức của mình:

func make_it_swim_and_quack(thing) -> void:
	print(thing.swim())
	print(thing.quack())
make_it_swim_and_quack(Duck.new())
make_it_swim_and_quack(Robot.new())
Duck swimming
Quack!
Robot swimming with propellers
Robot sound: BEEP BEEP!

Ghi đè cần có lớp cha chung, còn duck typing thì không. Với tham số có kiểu thing: Duck, truyền một Robot vào sẽ gây lỗi phân tích cú pháp.

Khi một đối tượng không có phương thức đó, lời gọi sẽ dừng hàm và báo lỗi như Invalid call. Nonexistent function 'quack'. Trước tiên, hãy kiểm tra bằng has_method(), tham số của hàm này là tên phương thức dưới dạng String:

var things := [Duck.new(), Rock.new()]
for thing in things:
	if thing.has_method("quack"):
		print(thing.quack())
	else:
		print("This one cannot quack")
Quack!
This one cannot quack

Cái giá phải trả: khi có kiểu dữ liệu, Godot phát hiện tên phương thức sai trước khi chương trình chạy; với kiểu gõ vịt, bạn chỉ phát hiện ra khi dòng lệnh chạy. Hãy dùng cách này cho các đối tượng thuộc những lớp không liên quan nhưng có chung một vài phương thức.

challenge icon

Thử thách

Dễ

Một vụ nổ gây sát thương cho mọi thứ ở gần có thể nhận sát thương. Các đối tượng không có lớp cha chung. player.gd và rock.gd đã hoàn tất: Player có take_damage(amount) và status(), còn Rock chỉ có status().

  • Trong crate.gd, hãy thêm cho Crate một biến broken ban đầu là false, một phương thức take_damage(amount) làm hỏng thùng khi lượng sát thương là 10 trở lên, và status() trả về Crate: broken hoặc Crate: intact.
  • Trong explosion.gd, hãy viết phương thức tĩnh hit_all(things, amount). Phương thức này gọi take_damage(amount) trên mọi đối tượng trong mảng có phương thức đó, rồi trả về số đối tượng bị đánh trúng.

Code được cung cấp đọc một dòng tên đối tượng (player, crate hoặc rock), sau đó đọc số vụ nổ và lượng sát thương của mỗi vụ. Code in ra số đối tượng bị mỗi vụ nổ đánh trúng, rồi in trạng thái của từng đối tượng. Với player crate rock và các vụ nổ gây sát thương 5 và 20, kết quả in ra là:

Explosion 1 hit 2 objects
Explosion 2 hit 2 objects
Player: 75 HP
Crate: broken
Rock: solid

Code của bạn đặt trong crate.gd và explosion.gd. main.gd chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

extends Node

func _ready():
	# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
	var things := []
	for word in OS.read_string_from_stdin().split(" "):
		if word == "player":
			things.append(Player.new())
		elif word == "crate":
			things.append(Crate.new())
		else:
			things.append(Rock.new())
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var hits := Explosion.hit_all(things, int(OS.read_string_from_stdin()))
		print("Explosion %d hit %d objects" % [i + 1, hits])
	for thing in things:
		print(thing.status())
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch GDScript online