Creational patterns organize object creation; behavioral patterns organize interactions and responsibility for behavior. A creational pattern controls how concrete software objects are selected, instantiated, or shared, so clients need not hard-code the entire construction policy. Examples include the factory method pattern and the singleton pattern.
A behavioral pattern describes how objects collaborate, receive requests, or distribute an algorithm's behavior. For example the observer pattern lets a subject notify dependants after its state changes, and the strategy pattern supplies interchangeable implementations of an operation. This distinction concerns the pattern's principal responsibility; a system can use both kinds together.
Introduce one shared
GymStandings singleton object. It stores the latest current-performance score for every participating GymEquipment, using the equipment reference as key, and returns every equipment object tied for the maximum score. Performance.update sends each freshly computed score to this object. The previous observer pattern and its prediction observer remain in use.The added
Performance -> GymStandings dependency means “reports to,” while GymStandings -> GymEquipment represents the equipment keys in its stored score map. GymStandings is an software object, so clients share its unique instance instead of constructing one standings object per machine. The singleton pattern centralizes the current scores and the maximum-score query. Equipment objects, rather than user names, are the keys because two users can have the same name. Scores are current readings, not an accumulation of all historical notifications; the largest reported score wins, with all ties retained.Use a Scala For example,
object for the singleton pattern, and replace Performance with a version that reports to it. This code plugs into the source's Equipment, GymEquipment, and Functionality definitions; Prediction can remain an independent observer once its displayed string line break is made executable.object GymStandings {
private var scores = Map.empty[GymEquipment, Int]
def record(e: GymEquipment, calories: Int): Unit = {
scores = scores.updated(e, calories)
}
def remove(e: GymEquipment): Unit = {
scores = scores - e
}
def leaders: List[(GymEquipment, Int)] = {
if (scores.isEmpty) Nil
else {
val best = scores.values.max
scores.toList.filter { case (_, score) => score == best }
.sortBy { case (equipment, _) => equipment.name }
}
}
}
class Performance(e: GymEquipment) extends Functionality {
private def calories: Int =
((e.age - e.weight + e.heartRate) * e.time) / 10
e.addFunctionality(this)
GymStandings.record(e, calories)
def update(): Unit = {
val current = calories
GymStandings.record(e, current)
println("Your current performance is: " + current + " calories burned.")
}
}new Performance(treadmill) and new Performance(bike) attach two observers; later GymStandings.leaders returns the leading equipment/score pairs. Initial registration records the equipment's present score, even before its first subsequent update. Recording replaces the same equipment's previous score, so repeated notifications do not multiply its contribution. The explicit remove supports taking an equipment object out of the comparison; detaching its observer when its session ends also prevents later re-registration through another notification. In the original single-threaded event model no extra synchronization is needed.All equipment observers report to the same standings software object, and
leaders returns every maximum or Nil if empty. The query takes to find/filter scores for active equipment objects, plus sorting the tied names for deterministic presentation by name; equal names may remain in either order and still denote distinct equipment. This uses the exact supplied integer score formula and its integer division; it does not substitute a different performance metric. A functioning unsubscribe operation, if required, must explicitly correct the source to _functionalities = _functionalities diff List(f). Singleton object 2026-10-31
A named software object instantiated once in the scope of its definition. A top-level Scala
object can implement the singleton pattern. Nested object definitions can have different instances for different enclosing instances, so the relevant scope matters.