Menu
CoddyTech

Mẫu trang trí

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 52 trên 65.

Mẫu Decorator bổ sung hành vi cho một đối tượng bằng cách bọc nó trong một đối tượng khác có cùng các phương thức. Mỗi lớp bọc gọi đối tượng bên trong rồi thêm phần riêng của mình, và bạn có thể xếp chồng các lớp bọc theo bất kỳ thứ tự nào.

# weapon.gd
class_name Weapon
extends RefCounted

func get_damage() -> int:
	return 10

func describe() -> String:
	return "Sword"

Bộ trang trí kế thừa từ Weapon, nên có thể thay thế một vũ khí ở bất cứ nơi nào cần đến vũ khí, đồng thời chứa vũ khí mà nó bọc:

# fire_enchant.gd
class_name FireEnchant
extends Weapon

var inner: Weapon

func _init(wrapped: Weapon) -> void:
	inner = wrapped

func get_damage() -> int:
	return inner.get_damage() + 5

func describe() -> String:
	return inner.describe() + " + Fire"

SharpEnchant được viết theo cách tương tự, nhưng get_damage() của nó trả về inner.get_damage() * 2 và describe() thêm " + Sharp".

# main.gd
var weapon: Weapon = Weapon.new()
print(weapon.describe(), ": ", weapon.get_damage())
weapon = FireEnchant.new(weapon)
weapon = SharpEnchant.new(weapon)
print(weapon.describe(), ": ", weapon.get_damage())
Sword: 10
Sword + Fire + Sharp: 30

Các lời gọi di chuyển vào bên trong: SharpEnchant gọi FireEnchant, rồi FireEnchant gọi thanh kiếm. Thanh kiếm trả về 10, lửa tăng giá trị lên 15, rồi phép sắc bén nhân đôi thành 30. Với thứ tự ngược lại, FireEnchant.new(SharpEnchant.new(Weapon.new())), sát thương là 10 * 2 + 5, tức là 25.

Nếu không có decorator, mỗi cách kết hợp sẽ cần một lớp con riêng: FireSword, SharpSword, FireSharpSword và nhiều lớp khác. Với decorator, hai lớp nhỏ có thể đáp ứng mọi cách kết hợp, còn mỗi bùa phép mới chỉ cần thêm một tệp.

challenge icon

Thử thách

Trung bình

Một quán cà phê pha đồ uống bằng các lớp trang trí. Beverage (beverage.gd) khai báo cost() và description(); Espresso (espresso.gd, $3) và Tea (tea.gd, $2) đã được viết sẵn. Mỗi lớp trang trí lưu đồ uống mà nó bọc trong inner (các hàm khởi tạo đã được viết sẵn). Ghi đè cost() và description() trong:

  • Milk: tăng giá thêm 1 và thêm + Milk vào phần mô tả.
  • Mocha: tăng giá thêm 2 và thêm + Mocha.
  • Large: nhân đôi giá của mọi thứ bên trong nó và đặt Large ở trước phần mô tả.

Code có sẵn đọc số lượng đơn hàng tiếp theo, rồi đọc mỗi đơn hàng trên một dòng: đồ uống (espresso hoặc tea) và các món thêm, áp dụng từ trái sang phải. Với espresso milk large và tea mocha milk, đầu ra là:

Large Espresso + Milk: $8
Tea + Mocha + Milk: $5

Viết code của bạn trong milk.gd, mocha.gd và large.gd. main.gd chứa code đầu vào/đầu ra có sẵn và bạn không thể chỉnh sửa tệp này.

Tự làm thử

extends Node

func _ready():
	# Đoạn code đầu vào/đầu ra đã có sẵn: bạn giữ nguyên nhé
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var words := OS.read_string_from_stdin().split(" ")
		var drink: Beverage = Tea.new()
		if words[0] == "espresso":
			drink = Espresso.new()
		for extra in words.slice(1):
			match extra:
				"milk":
					drink = Milk.new(drink)
				"mocha":
					drink = Mocha.new(drink)
				"large":
					drink = Large.new(drink)
		print("%s: $%d" % [drink.description(), drink.cost()])
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