Structured Programming – Shapes, Design Recipe & Testing

Administrative Announcements

  • Additional TutA tutorial slots have been opened in MyTimeTable
    • Action: enroll as soon as possible
  • Class representatives
    • Applications close Friday COB (close-of-business)
  • Assignment A1
    • Specification released — recommended to start immediately
    • Web form (released later this week) will let you …
    • register for mandatory code-walks
    • optionally state date/time preferences for the walk
    • optionally request extension tokens
    • Form can be edited/resubmitted any number of times before the deadline
    • Warning: do not procrastinate, otherwise you may forget to register
  • Administrative footers on every slide (TEQSA provider, CRICOS code, etc.)
    • Implication: ANU compliance & accreditation context

Problem Statement Introduced in This Lecture

  • Design a program that handles geometric shapes of three kinds:
    • Circle
    • Rectangle
    • Right Triangle (right-angled triangle)
  • Provide two general-purpose functions that accept any shape instance and compute
    • its area
    • its perimeter (a.k.a. circumference for circles)

The Design Recipe (6-Step Methodology)

  • Step 1 — Problem Analysis & Data Design
  • Step 2 — Function Signature & Purpose Statement
  • Step 3 — Examples / Tests-as-specification
  • Step 4 — Design Strategy (how will we actually solve it?)
  • Step 5 — Implementation (coding phase)
  • Step 6 — Tests (turn examples into automated checks)
  • Key pedagogical motto: “The shape of the data determines the shape of the code.”
Step 1 • Data Design — Review of Data Modelling Tools
  • Basic data types (primitives such as int, double, boolean, String)
  • Enumerations (enum) for finite sets of distinct values without associated data
  • Records (record) for compound data with fixed fields
  • NEW: General Itemizations
    • Use when there are multiple distinct variants each with their own payload
      (a.k.a. algebraic data type / tagged union / sum type)
    • Java 21+ mechanism: sealed interface + one record per case
General Itemization Pattern (Shapes Case Study)
  • Declare a sealed interface to name the whole family and to enumerate all legal variants
  /** A Shape represents a geometric shape and is one of:
   *  - Circle
   *  - Rectangle
   *  - RightTriangle
   */
  sealed interface Shape permits Circle, Rectangle, RightTriangle {}
  • Supply one record for each variant (must implements Shape)
  /** A Circle is a Shape characterized by its radius */
  record Circle(double radius) implements Shape {}
  /** A Rectangle is a Shape characterized by width & height */
  record Rectangle(double width, double height) implements Shape {}
  /** A RightTriangle is a Shape characterized by its two legs */
  record RightTriangle(double leftLeg, double rightLeg) implements Shape {}
  • Documentation block should mention examples (e.g. “circle of radius 1.01.0”).
  • Radius, width, height, leg lengths expected to be non-negative (domain constraint).
  • Ethical / practical note: always capture invariants (e.g.
    “negative length ➔ illegal argument”).
Templates for Functions Over Itemizations
  • Use Java 21 pattern-matching switch + deconstruction patterns
    Template skeleton supplied on slide 11:
  // { ...return ...
  switch(x) {
    case Circle(var radius)        -> ...;
    case Rectangle(var width, var height) -> ...;
    case RightTriangle(var leftLeg, var rightLeg) -> ...;
  }
  ...;
  // }
  • Guarantees exhaustiveness; compiler warns if a new Shape subtype is added but cases are missing.
  • Mirrors the Design Recipe principle that data drives control flow.

Step 2 • Function Signature & Purpose Statement

  • We require two total functions (defined for every possible Shape):
    1. double area(Shape s) — returns the numeric area of s.
    • Purpose example: “Computes area(s)\text{area}(s) measured in square units consistent with the shape’s fields.”
    1. double perimeter(Shape s) — returns the numeric perimeter of s.
    • Purpose example: “Computes perimeter(s)\text{perimeter}(s); for circles this is the circumference.”

Step 3 • Examples (Executable Specifications)

  • Guidelines
    • Cover all major scenarios (one per variant, plus edge cases such as zero-sized shapes).
    • Use precise literals where feasible; otherwise give concrete natural-language description.
  • Suggested test table (slide 20 exercise):
    • Circle r=1r = 1
    • Expected area =π= \pi
    • Expected perimeter =2π= 2\pi
    • Rectangle w=2, h=3w = 2,\ h = 3
    • Area =6= 6
    • Perimeter =2×(2+3)=10= 2\times(2+3)=10
    • RightTriangle a=3, b=4a = 3,\ b = 4
    • Area =12ab=6= \tfrac12 ab = 6
    • Perimeter =a+b+c= a + b + c where c=a2+b2=5c = \sqrt{a^2 + b^2} = 5 so perimeter =12= 12
    • Degenerate cases (optional but good practice) e.g. rectangle w=0,h=5w=0,h=5 ➔ area 00.
Example Documentation Style Patterns (slides 17–19)
  • When primitives involved: "given 5,2 expect 7".
  • When complex or visual state involved: natural-language but still concrete.
  • Records (Vector2D, State) used to illustrate example clarity vs. feasibility trade-offs.

Step 4 • Design Strategy (How to Implement?)

  • Four canonical strategies (review):
    1. Simple Expressions — constant or direct formula.
    2. Combining Functions — compose existing helpers.
    3. Case Distinction — conditional branching (if/switch).
    4. Template Application — mechanically follow template for the data definition.
  • For area / perimeter we pick Case Distinction via Template Application because
    we must branch on the kind of Shape.

Step 5 • Implementation Snapshot (Live Demo Mentioned)

  • Implementation skeleton:
  double area(Shape s) {
    return switch(s) {
      case Circle(var r)             -> Math.PI * r * r;
      case Rectangle(var w, var h)   -> w * h;
      case RightTriangle(var a,var b)-> 0.5 * a * b; // right-triangle area formula
    };
  }
  double perimeter(Shape s) {
    return switch(s) {
      case Circle(var r)             -> 2 * Math.PI * r;
      case Rectangle(var w, var h)   -> 2 * (w + h);
      case RightTriangle(var a,var b)-> a + b + Math.hypot(a,b);
    };
  }
  • Note: Math.hypot(a,b) computes a2+b2\sqrt{a^2+b^2} robustly.
  • Ensure input validation if negative parameters are disallowed (either throw or require pre-condition).

Step 6 • Tests — Motivation & Mechanics

  • Why test?
    • Confidence that code meets specification.
    • Safety net against future refactors (regression testing).
  • What to test?Everything! (except outputs too complex, e.g. raw Image).
    • In COMP1110/1140/6710: all functions mandated by Design Recipe whose result is not Image.
  • Test Coverage considerations
    • Start by converting examples into test cases.
    • Ensure every code path, branch, and helper is executed at least once.
  • Testing hierarchy (3-levels)
    1. test() function — orchestrator.
    2. Test case functions — one per scenario, preceded by comment explaining intent.
    3. Assertions inside test case — testEqual, testNotEqual, testTrue, testFalse.
  • Example from slides:
  /** Tests that the sum of 5 and 2 is 7 */
  void testSumExample1() {
    testEqual(7, sum(5,2), "5 + 2 should be 7");
  }
  void test() {
    runAsTest(this::testSumExample1);
  }
  • Running tests (Java preview features enabled):
  java --enable-preview comp1110.testing.Test yourFile.java
  • Exercise continuation: write analogous tests for area/perimeter using examples listed earlier.

Universe Package & Functional Images (Roadmap for Future Lectures)

  • Universe package supports World programs (a.k.a. interactive animations).
    • Core concepts: Image type, functions that produce/transform images, BigBang execution model.
  • Image Basics
    • Every Image has a bounding box (width, height in pixels) and a pin (default centre).
    • Coordinate system origin: top-left; xx rightwards, yy downwards.
  • Base Image Constructors
    • Rectangle, Circle, Ellipse, Polygon, Text, Image (file).
    • Styling: fill vs. outline, colours.
  • Image Combinators
    • overlay / overlayXY — place one image on top of another using pin alignment.
    • place / placeXY — like overlay but bounding box stays that of base image (top image may be clipped).
    • Transformations: rotate, scale, etc.
  • BigBang Setup Functions (minimal set)
  Image draw(World w);
  World  step(World w);              // optional, called 30 Hz
  World  onKeyEvent(World w, KeyEvent kev);    // optional
  World  onMouseEvent(World w, MouseEvent mev); // optional
  boolean hasEnded(World w);         // optional
  • Launching a world
  void main() {
    var initial = ...;
    BigBang("Title", initial, this::draw, this::step, this::onKeyEvent,
            this::onMouseEvent, this::hasEnded);
  }
  • Execution timeline: 30 step-draw cycles per second; user events may fire asynchronously in between cycles.

Broader Connections & Significance

  • Sealed interfaces + records show modern Java catching up with algebraic data types common in functional languages (Haskell, ML, Scala).
  • Encourages total, expression-oriented style where every switch is exhaustive and every function handles all cases — fewer nulls, fewer default: bugs.
  • Design Recipe brings discipline: separates thinking (analysis → examples → strategy) from typing code.
  • Testing culture emphasised early, aligning with professional software engineering best practices (CI, regression suites).
  • Real-world relevance: geometric computation ubiquitous (graphics, CAD, physics engines) — correctness crucial.

Numerical & Mathematical References

  • Circle: area A=πr2A = \pi r^2; perimeter P=2πrP = 2\pi r.
  • Rectangle: area A=w×hA = w\times h; perimeter P=2(w+h)P = 2(w+h).
  • Right Triangle: hypotenuse c=a2+b2c = \sqrt{a^2 + b^2}; area =12ab= \tfrac12 ab; perimeter =a+b+c= a+b+c.
  • Test vector magnitude example: (x,y)=x2+y2| (x,y) | = \sqrt{x^2+y^2} (slide 19 context).

Practical & Ethical Reminders

  • Data invariants (non-negative lengths) must be enforced; unchecked invalid values propagate silently and create latent bugs.
  • Clear documentation (examples, purpose statements) aids human maintainers and future you; integral part of professional responsibility.
  • Adequate test coverage is both a quality measure and an ethical safeguard against harmful software defects.