Menu
CoddyTech

Adapter Pattern

Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 51 of 65.

The Adapter pattern lets an object work with code that expects different methods. The adapter wraps the object and turns each expected call into a call the object understands.

An old class from an earlier project stores health as two numbers:

# old_monster.gd
class_name OldMonster
extends RefCounted

var life := 30
var max_life := 120

Your HUD shows anything that has get_label() and get_hp_percent(). A Knight already has both (it returns "Knight" and 80), but OldMonster has neither:

# health_display.gd
class_name HealthDisplay
extends RefCounted

func show_health(target) -> void:
	print(target.get_label(), ": ", target.get_hp_percent(), "%")

The adapter holds an OldMonster and provides the two methods the display calls:

# monster_adapter.gd
class_name MonsterAdapter
extends RefCounted

var monster: OldMonster

func _init(wrapped: OldMonster) -> void:
	monster = wrapped

func get_label() -> String:
	return "Monster"

func get_hp_percent() -> int:
	return monster.life * 100 / monster.max_life
# main.gd
var display := HealthDisplay.new()
display.show_health(Knight.new())
display.show_health(MonsterAdapter.new(OldMonster.new()))
Knight: 80%
Monster: 25%

Neither OldMonster nor HealthDisplay changed. That matters when you cannot or should not edit a class: it comes from a plugin, another team owns it, or many scripts depend on its names.

The adapter forwards each call and converts the data on the way, here two numbers into one percentage. Multiply before dividing: with integers, 30 / 120 * 100 is 0, while 30 * 100 / 120 is 25.

challenge icon

Challenge

Easy

A weather board shows sensors that have get_sensor_name() and get_celsius(), like ModernSensor (modern_sensor.gd). The board is in weather_board.gd. Old thermometers (legacy_thermometer.gd) only offer read_fahrenheit() and get_code(). Write the adapter ThermometerAdapter:

  • The constructor stores the wrapped LegacyThermometer.
  • get_sensor_name() returns Thermometer, a space and the thermometer's code, like Thermometer TH-4.
  • get_celsius() returns the temperature in Celsius: (fahrenheit - 32) * 5 / 9, as a float.

Do not change the other classes. The supplied code reads how many sensors follow, then one per line: legacy ID FAHRENHEIT or modern NAME CELSIUS. It wraps every legacy thermometer in your adapter and shows all sensors. For legacy 4 98.6 and modern Garden 21.5 the output is:

Thermometer TH-4: 37.0 C
Garden: 21.5 C
Warmest: Thermometer TH-4

Your code goes in thermometer_adapter.gd. main.gd holds the supplied input/output code and cannot be edited.

Try it yourself

extends Node

func _ready():
	# Supplied input/output code: keep it as it is
	var board := WeatherBoard.new()
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "legacy":
			var old := LegacyThermometer.new(int(parts[1]), float(parts[2]))
			board.add_sensor(ThermometerAdapter.new(old))
		else:
			board.add_sensor(ModernSensor.new(parts[1], float(parts[2])))
	board.show_all()
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online GDScript compiler