Mẫu Strategy
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 49 trên 65.
Mẫu Strategy đặt mỗi phiên bản của một thuật toán trong một đối tượng riêng, nhờ đó bạn có thể thay đổi thuật toán khi chương trình đang chạy. Đối tượng sử dụng thuật toán đó, gọi là ngữ cảnh, gọi một phương thức và không cần quan tâm nó đang giữ phiên bản nào.
Đây là hai chiến lược tấn công, một lớp cha và một lớp con có cùng phương thức damage():
# attack_style.gd
class_name AttackStyle
extends RefCounted
func damage(power: int) -> int:
return power# heavy_attack.gd
class_name HeavyAttack
extends AttackStyle
func damage(power: int) -> int:
return power * 2Ngữ cảnh lưu một chiến lược và giao việc tính toán cho chiến lược đó:
# fighter.gd
class_name Fighter
extends RefCounted
var power := 10
var style: AttackStyle = AttackStyle.new()
func set_style(new_style: AttackStyle) -> void:
style = new_style
func attack() -> void:
print("Hit for ", style.damage(power))# main.gd
var fighter := Fighter.new()
fighter.attack()
fighter.set_style(HeavyAttack.new())
fighter.attack()Hit for 10
Hit for 20Fighter không bao giờ kiểm tra mình đang giữ kiểu nào. Mỗi kiểu mới là một lớp mới, còn fighter.gd vẫn giữ nguyên; một chuỗi if style == "heavy" bên trong attack() sẽ dài thêm với mỗi kiểu mới.
Khi một chiến lược chỉ là một hàm nhỏ không có dữ liệu riêng, một Callable là đủ. Ngữ cảnh lưu hàm và chạy hàm bằng call():
# price_calculator.gd
class_name PriceCalculator
extends RefCounted
var discount: Callable = func(price): return price
func final_price(price: int) -> int:
return discount.call(price)# main.gd
var calc := PriceCalculator.new()
print(calc.final_price(80))
calc.discount = func(price): return price / 2
print(calc.final_price(80))
calc.discount = func(price): return max(price - 25, 0)
print(calc.final_price(80))80
40
55Dùng lớp (class) khi một chiến lược có cài đặt riêng hoặc nhiều phương thức, và dùng Callable cho một phép tính ngắn. Bạn đã từng dùng ý tưởng này: hàm so sánh mà bạn truyền vào sort_custom() chính là một chiến lược sắp xếp.
Thử thách
Trung bìnhGiỏ hàng của cửa hàng có thể đổi chiến lược giảm giá khi thanh toán. Discount trong discount.gd là chiến lược cơ sở, không giảm giá. Hãy viết thêm hai chiến lược và giỏ hàng:
PercentOff(được tạo với một tỷ lệ phần trăm):apply(total)trả vềtotal - total * percent / 100(phép toán số nguyên), cònlabel()trả về10% offkhi tỷ lệ là 10 phần trăm.FixedOff(được tạo với một số tiền):apply(total)trả về tổng trừ đi số tiền đó, nhưng không bao giờ thấp hơn0, cònlabel()trả về30 offkhi số tiền là 30.Cart:set_discount(new_discount)lưu chiến lược, còncheckout()cộng các mức giá và trả vềSubtotal TOTAL, LABEL, pay PRICE, trong đó PRICE là giá trị màapply()của chiến lược trả về.
Code được cung cấp đọc giá trên một dòng, số lượt thanh toán tiếp theo, rồi đọc một chiến lược trên mỗi dòng: percent N, fixed N hoặc none. Với các mức giá 50 30 40 và các chiến lược percent 10, fixed 30, none, đầu ra là:
Subtotal 120, 10% off, pay 108
Subtotal 120, 30 off, pay 90
Subtotal 120, no discount, pay 120Code của bạn nằm trong cart.gd, percent_off.gd và fixed_off.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 cart := Cart.new()
for price in OS.read_string_from_stdin().split(" "):
cart.add_item(int(price))
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "percent":
cart.set_discount(PercentOff.new(int(parts[1])))
elif parts[0] == "fixed":
cart.set_discount(FixedOff.new(int(parts[1])))
else:
cart.set_discount(Discount.new())
print(cart.checkout())
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
1Nền tảng về OOP
Tệp bên ngoàiGiới thiệu về OOPLớp và đối tượngTừ khóa selfPhương thứcThuộc tínhHàm khởi tạo (_init)Ôn tập: máy tính đơn giản4Tính đa hình
Xem lại cách ghi đè phương thứcĐịnh kiểu vịtLớp và phương thức trừu tượngThiết kế interfaceÔn tập: máy tính hình học7Tín hiệu
Tín hiệu là gì?Khai báo và phát tín hiệuKết nối với tín hiệuTín hiệu có đối sốÔn tập: hệ thống sức khỏe10Các mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổng hợp2Thuộc tính của lớp
Biến instance và biến staticHàm staticPhương thức thiết lập thuộc tínhPhương thức lấy thuộc tínhQuy ước về tính riêng tưÔn tập: trình quản lý tài khoản ngân hàng5Tính đóng gói
Thành viên công khai và riêng tưXác thực bằng phương thức setterẨn thông tinThuộc tính chỉ đọcÔn tập: hệ thống hồ sơ học sinh8Khái niệm OOP nâng cao
Kết hợp và kế thừaLớp bên trongNode dưới dạng thành phầnRefCounted và NodeEnum trong lớpTự luyện tập: Trình biên dịch GDScript online