Menu
CoddyTech

コンポジションと継承

CoddyのGDScriptジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 40/65。

クラスは2つの方法でコードを再利用します。継承(extends)は、あるクラスが別のクラスの一種であることを示します。つまり、MageはPlayerです。コンポジションは、あるクラスが一部を持つことを示します。メンバー変数に別のオブジェクトを格納し、そのオブジェクトに処理を委ねます:

# motor.gd
class_name Motor
extends RefCounted

func start() -> String:
	return "motor running"

# car.gd
class_name Car
extends RefCounted

var motor := Motor.new()

func start() -> String:
	return "Car starts, " + motor.start()

Car.new().start()はCar starts, motor runningを返します。車はモーターの仕組みを知りません。motor.start()を呼び出します。このように呼び出しを部品に任せることを委譲と呼びます。(EngineはすでにGodotのクラスなので、クラス名はMotorです。)

部品が変更されたり組み合わされたりするとき、コンポジションの真価が発揮されます。継承では、歩くキャラクターと飛ぶキャラクターは2つのサブクラスであり、飛び始めるコウモリはクラスを変更できません。コンポジションでは、キャラクターは移動の部品を持ち、それを切り替えるのは1つの代入です:

# character.gd
class_name Character
extends RefCounted

var char_name: String
var movement: Movement

func _init(new_name: String, new_movement: Movement) -> void:
	char_name = new_name
	movement = new_movement

func move() -> String:
	return char_name + " " + movement.move()

# main.gd
var bat := Character.new("Bat", Walk.new())
print(bat.move()) # Batが歩く
bat.movement = Fly.new()
print(bat.move()) # Batが飛ぶ

ここでは、WalkとFlyが小さなMovementクラスを拡張しており、2つの手法が連携して動作します。GDScriptのクラスは1つのクラスしかextendできないため、複数のパーツを保持することで、1つのオブジェクトに複数の能力を持たせる方法としてもコンポジションが使われます。

決して変わることのない実際の「〜である」という関係には継承を使いましょう。クラスが何かを「持つ」場合や、ゲームの実行中にその部分を交換する必要がある場合は、コンポジションを使いましょう。

challenge icon

チャレンジ

簡単

騎士は武器を継承するのではありません。騎士は武器を持っており、武器を切り替えることができます。両方のクラスを作成しましょう。

  • Weapon は weapon_name と damage を保持し、どちらもコンストラクターで設定します: Weapon.new("Sword", 7)。
  • Knight は knight_name(コンストラクターから設定)と武器を保持します。武器は最初は null です。
  • equip(new_weapon) は武器を置き換え、drop() は武器を null に戻します。
  • attack() は、騎士の名前と武器の名前およびダメージを使って Ada attacks with Sword for 7 damage を返します。武器がない場合は Ada has no weapon を返します。

用意されたコードは騎士の名前と後続のコマンド数(equip Sword 7、drop、attack)を読み取り、それらを実行します。Ada と attack、equip Sword 7、attack、equip Axe 12、attack、drop、attack の場合、次のように出力されます:

Ada has no weapon
Equipped Sword
Ada attacks with Sword for 7 damage
Equipped Axe
Ada attacks with Axe for 12 damage
Dropped the weapon
Ada has no weapon

コードは weapon.gd と knight.gd に記述します。main.gd には用意された入出力コードが含まれており、編集できません。

自分で試してみよう

extends Node

func _ready():
	# 用意された入出力コード: そのままにしておく
	var knight := Knight.new(OS.read_string_from_stdin())
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "equip":
			knight.equip(Weapon.new(parts[1], int(parts[2])))
			print("Equipped ", parts[1])
		elif parts[0] == "drop":
			knight.drop()
			print("Dropped the weapon")
		else:
			print(knight.attack())
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: GDScriptオンラインコンパイラ