How do I implement the strategy pattern in EK9?

← Design Patterns and Idioms · Ref: Q214

EK9 implements the strategy pattern using abstract functions and dynamic function implementations. Pass different function instances to swap behavior.

ABSTRACT FUNCTION AS STRATEGY

Define the strategy interface as an abstract function:

  Formatter as abstract function
    -> item as String
    <- rtn as String?

DYNAMIC IMPLEMENTATIONS

Create concrete strategies with dynamic functions:

  upperFormatter <- () is Formatter as function
    rtn: item.upperCase()
  lowerFormatter <- () is Formatter as function
    rtn: item.lowerCase()

PROCESSOR WITH STRATEGY

Pass the strategy as a parameter:

  Processor
    process()
      ->
        formatter as Formatter
        text as String
      <- rtn as String: formatter(text)

SWAPPING STRATEGIES

Use different formatters without changing Processor:

  processor.process(upperFormatter, "hello")
  processor.process(lowerFormatter, "HELLO")

See Q57 for strategy without subclassing. See Q52 for dynamic functions. See Q55 for function delegation. See Q210 for trait delegation as an alternative to function strategies.

Example

defines module qa.patterns.strategy

  defines function

    Formatter() as abstract
      -> item as String
      <- rtn as String?

  defines class

    TextProcessor
      process()
        ->
          formatter as Formatter
          text as String
        <- rtn as String: formatter(text)

      default operator ?

  defines program

    StrategyPatternDemo()
      stdout <- Stdout()

      // === DYNAMIC FUNCTION STRATEGIES ===

      upperFormatter <- () is Formatter as function
        rtn: item.upperCase()

      lowerFormatter <- () is Formatter as function
        rtn: item.lowerCase()

      // === PROCESSOR USES STRATEGY ===

      textProcessor <- TextProcessor()

      upper <- textProcessor.process(upperFormatter, "hello world")
      stdout.println(`Upper: ${upper}`)

      lower <- textProcessor.process(lowerFormatter, "HELLO WORLD")
      stdout.println(`Lower: ${lower}`)

      // === SWAP STRATEGIES FREELY ===

      stdout.println("Strategy pattern: swap behavior via function parameters")

Common mistakes

E07110 — Abstract functions define a strategy interface with no implementation. Using 'as open' instead of 'as abstract' triggers E07110 because 'open' is for functions with a body that can be overridden, not for bodyless declarations. See ek9 -h E07110 for details.

Incorrect:

Formatter() as open

Correct:

Formatter() as abstract
Other ways to ask this
  • How do I swap algorithms at runtime in EK9?
  • How do I use functions as strategies in EK9?
  • How do I inject behavior via function parameters in EK9?

Coming from another language?

Java: Strategy interface with implementing classes. Python: pass functions directly (first-class). Rust: trait objects or closures. Go: function types. Kotlin: function types or interface implementations. EK9: abstract function as strategy interface, dynamic functions as implementations.

Keywords: function, delegate, behavior, strategy, algorithm, design, inject, flexible, swap, pattern, idiom