M9 — Functional Design

SENG 365 — Software Engineering

Neil Ernst

University of Victoria

2026-10-21

Module Goals

Design Judgment

By the end of this module, you should be able to:

  • recognize behavior parameterization hidden inside small classes;
  • choose among a named class, lambda, method reference, and functional interface;
  • transform imperative collection processing into a declarative pipeline; and
  • criticize functional code that is concise but obscures intent.

Functional Interfaces as Design Tools

Preparation

Before class: Read Chapter 9, Sections 9.1–9.5.

Entry Decision

final class ByBrakingDistance implements Comparator<Car> {
    public int compare(Car left, Car right) {
        return Double.compare(
            left.brakingDistance(),
            right.brakingDistance());
    }
}

What is the object contributing besides one piece of behavior?

Behavior as a Value

fleet.sort((left, right) ->
    Double.compare(
        left.brakingDistance(),
        right.brakingDistance()));

A functional interface gives a function a Java type. Lambdas and method references can supply values of that type without a named class.

Conciseness is useful only when the behavior remains legible.

Transform the Design

Given separate classes for:

  • comparing cars by acceleration;
  • filtering cars by electric range; and
  • formatting a car label;

produce a before/after design using suitable library functional interfaces.

next steps

For each transformation, decide whether to use:

  • a lambda;
  • a method reference;
  • a named function object; or
  • an application-defined functional interface.

Library or Domain Type?

Predicate<Car>
Function<Car, String>
Comparator<Car>

Library types maximize familiarity and composition.

@FunctionalInterface
interface SafetyRule {
    boolean permits(Car car);
}

An application type can communicate domain intent and constrain accidental use.

Strategy and Command Revisited

A small stateless Strategy or Command may collapse into a function value.

A named class may still be better when the behavior:

  • owns state;
  • has several related operations;
  • needs a strong domain name;
  • is configured in multiple steps; or
  • must be inspected, serialized, or managed as an object.

Functional style complements object-oriented design; it does not erase design forces.

Compare Alternatives

  • Is the lambda short and self-documenting?
  • Would a method name explain it better?
  • Does a domain-specific interface add useful constraint?
  • Is captured mutable state hidden inside the closure?
  • Will the behavior be reused or tested independently?
  • Did fewer lines also mean fewer concepts?

Exit Note

A named class communicates intent better than a lambda when __________.

Streams and Declarative Design

Preparation

Before class: Read Chapter 9, Section 9.6, and review the Chapter 9 Insights.

Entry Decision

What does this loop compute?

List<String> labels = new ArrayList<>();
for (Card card : cards) {
    if (card.isFaceCard()) {
        labels.add(card.rank().name());
    }
}
labels.sort(String::compareTo);

How long did it take to separate traversal mechanics from intent?

A Declarative Candidate

List<String> labels = cards.stream()
    .filter(Card::isFaceCard)
    .map(card -> card.rank().name())
    .sorted()
    .toList();

The pipeline names transformations. That does not automatically make it clearer.

Pipeline Studio

Transform collection-processing code that:

  • selects black face cards;
  • orders them by rank;
  • maps them to display labels; and
  • groups or counts them by suit.

Produce the imperative and functional versions side by side. Annotate each stream stage with its input and output type.

Readability Critique

Evaluate both versions:

  • Where is intent clearest?
  • Are side effects hidden in map or peek?
  • Is the pipeline short enough to understand as one expression?
  • Would a named helper improve a lambda?
  • Does collecting create an unnecessary intermediate structure?
  • Would an ordinary loop better express early exit or complex state?

The goal is declarative design, not maximum chaining.

Course Extension: Functional Properties

These ideas extend beyond the chapter’s Java mechanisms:

  • A pure function has no observable side effects.
  • An immutable value cannot change unexpectedly.
  • An idempotent operation has the same effect when repeated.

resizeTo100px(image) is idempotent; shrinkByHalf(image) is not. This distinction matters when distributed work may be retried.

Cube Composer

Retain the visual transfer exercise: Cube Composer.

While solving a level, identify:

  • the value flowing between operations;
  • a composed function;
  • any state or side effect; and
  • whether the solution reads as what to compute or how to step through it.

Use it to transfer the reasoning, not merely to finish the puzzle.

Beyond OO

Objects organize state and message passing; functions organize transformations and behavior composition. Actor, CSP, and reactive systems provide still other organizing models.

The course goal is not allegiance to a paradigm. It is recognizing which structure makes dependencies and change easiest to reason about.

Exit Note

A stream improves intent when __________; an ordinary loop is clearer when __________.