Decorator Pattern
Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 52 of 65.
The Decorator pattern adds behavior to an object by wrapping it in another object with the same methods. Each wrapper calls the object inside it and adds its own part, and wrappers can be stacked in any order.
# weapon.gd
class_name Weapon
extends RefCounted
func get_damage() -> int:
return 10
func describe() -> String:
return "Sword"A decorator extends Weapon, so it can go wherever a weapon is expected, and it holds the weapon it wraps:
# 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 is written the same way, but its get_damage() returns inner.get_damage() * 2 and its describe() adds " + 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: 30The calls travel inward: SharpEnchant asks FireEnchant, which asks the sword. The sword returns 10, fire makes it 15, and sharp doubles it to 30. In the other order, FireEnchant.new(SharpEnchant.new(Weapon.new())), the damage is 10 * 2 + 5, which is 25.
Without decorators, every combination would need its own subclass: FireSword, SharpSword, FireSharpSword and so on. With them, two small classes cover every combination, and a new enchantment is one more file.
Challenge
MediumA coffee shop builds drinks with decorators. Beverage (beverage.gd) declares cost() and description(); Espresso (espresso.gd, $3) and Tea (tea.gd, $2) are written. Each decorator stores the drink it wraps in inner (the constructors are written). Override cost() and description() in:
Milk: costs1more and adds+ Milkto the description.Mocha: costs2more and adds+ Mocha.Large: doubles the cost of everything inside it and putsLargein front of the description.
The supplied code reads how many orders follow, then one order per line: the drink (espresso or tea) and its extras, applied from left to right. For espresso milk large and tea mocha milk the output is:
Large Espresso + Milk: $8
Tea + Mocha + Milk: $5Your code goes in milk.gd, mocha.gd and large.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 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()])
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe self KeywordMethodsPropertiesConstructor (_init)Recap - Simple Calculator4Polymorphism
Method Overriding RevisitedDuck TypingAbstract Classes and MethodsInterface DesignRecap - Shape Calculator7Signals
What is a Signal?Declaring and EmittingConnecting to SignalsSignals with ArgumentsRecap - Health System10Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite Pattern2Class Properties
Instance vs Static VariablesStatic FunctionsProperty SettersProperty GettersPrivate by ConventionRecap - Bank Account Manager5Encapsulation
Public and Private MembersValidation with SettersInformation HidingRead-Only PropertiesRecap - Student Records System8Advanced OOP Concepts
Composition vs InheritanceInner ClassesNodes as ComponentsRefCounted vs NodeEnums in Classes11Project: Library Management
Project OverviewBook and User Classes3Inheritance
Basic InheritanceThe super KeywordMethod OverridingInheritance ChainsType Checks with isRecap - Employee Hierarchy6Special Methods
Virtual Methods IntroductionThe _to_string MethodCustom IteratorsRecap - Custom List9Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy PatternPractice on your own: Online GDScript compiler