Menu
CoddyTech

抽象类和方法

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

有些类的存在只是为了被扩展。每种形状都有面积,但只有知道形状是圆形还是矩形后,才能计算面积。将这类类标记为 @abstract,并写在 class_name 上方一行:

# shape.gd
@abstract
class_name Shape
extends RefCounted

@abstract func area() -> float
@abstract func perimeter() -> float

func describe() -> String:
	return "Area %.1f, perimeter %.1f" % [area(), perimeter()]

@abstract func 有签名但没有函数体:没有冒号,后面也没有代码。它表示每个子类都必须提供这个方法。describe() 是一个普通方法,子类会继承它,而且它可以直接调用抽象方法。

你不能创建抽象类的对象。Shape.new() 会在程序开始前停止运行,并显示 Parse Error: Cannot construct abstract class "Shape".

实现了所有抽象方法的子类是一个普通类:

# rectangle.gd
class_name Rectangle
extends Shape

var width: float
var height: float

func _init(w: float, h: float) -> void:
	width = w
	height = h

func area() -> float:
	return width * height

func perimeter() -> float:
	return 2 * (width + height)
var rect := Rectangle.new(4.0, 6.0)
print(rect.describe())
Area 24.0, perimeter 20.0

如果子类漏掉了其中一个,Godot 会在运行前报告错误:Class "Square" must implement "Shape.perimeter()" and other inherited abstract methods or be marked as "@abstract"。因此,你创建的每个对象都有 area() 和 perimeter()。

抽象类仍然可以有变量和 _init()。子类仍像往常一样用 super() 调用它。

challenge icon

挑战

简单

一家商店接受两种付款方式。每笔付款都有金额和收据,但手续费和名称取决于付款方式。

  • 将 PaymentMethod 设为抽象类,并声明两个抽象方法:fee() -> float 和 method_name() -> String。编写 receipt(),使其返回 NAME: paid AMOUNT, fee FEE, total TOTAL,每个数字都保留两位小数(总金额为金额加手续费)。
  • CreditCard:手续费为金额的 3%(amount * 0.03),名称为 Credit card。
  • BankTransfer:手续费为 1.5;金额大于或等于 100 时,手续费为 0.0,名称为 Bank transfer。

子类使用 PaymentMethod 的构造函数。提供的代码会读取后续付款的数量,然后为每笔付款读取一行:card AMOUNT 或 transfer AMOUNT。它会输出每张收据以及手续费总和。对于 card 50 和 transfer 120,输出为:

Credit card: paid 50.00, fee 1.50, total 51.50
Bank transfer: paid 120.00, fee 0.00, total 120.00
Total fees: 1.50

你的代码应写在 payment_method.gd、credit_card.gd 和 bank_transfer.gd 中。main.gd 存放了提供的输入/输出代码,不能修改。

亲手试试

extends Node

func _ready():
	# 提供的输入输出代码:保持原样
	var count := int(OS.read_string_from_stdin())
	var total_fees := 0.0
	for i in count:
		var p := OS.read_string_from_stdin().split(" ")
		var payment: PaymentMethod
		if p[0] == "card":
			payment = CreditCard.new(float(p[1]))
		else:
			payment = BankTransfer.new(float(p[1]))
		print(payment.receipt())
		total_fees += payment.fee()
	print("Total fees: %.2f" % total_fees)
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: GDScript 在线编译器