Lectures
Lecture 1: The Essence of Objects
Lecture 2: Unions of Objects
Lecture 3: Classes of Objects:   Data Definitions
Lecture 4: Classes of Objects:   Interface Definitions
Lecture 5: Interface Design:   Independent and Extensible
Lecture 6: Parametric Interface Definitions and Methods
Lecture 7: Introducing Java:   Syntax and Semantics
Lecture 8: Union, Interfaces, and Lists in Java
Lecture 9: Testing in Java
Lecture 10: Parametric Interfaces in Java
Lecture 11: Computations on Many Structural Arguments:   Double Dispatch
Lecture 12: Parameterized Types and Double Dispatch; Abstracting Values
Lecture 13: Abstracting Computation with Function Objects
Lecture 14: Function Objects & Parameterized Types; Anonymous Classes & Lambda
Lecture 15: The Fundamental List Abstraction:   Fold
Lecture 17: Midterm Review
Lecture 16: Properties of Equality:   Reflexive, Symmetric, Transitive, and Total
Lecture 19: Structural Equality with Double Dispatch; Abstracting and Overridding
Lecture 18: More Double Dispatch
Lecture 22: Optional, Maps, Sets, and Lifting Default Code to Abstract Classes
Lecture 23: The Visitor Pattern
Lecture 24: Implementing Visitors; Bank Accounts
Lecture 25: Imperatives:   Implicit Communication via Side-Effects
Lecture 26: Aside:   List Exercises
Lecture 27: Imperatives:   Cyclic Data
Lecture 28: Imperatives:   Methods over Cylic Data
Lecture 29: BSTs, Maps, The Law of Hash  Code, and Comparable vs Comparators
Lecture 30: Random access and Array  Lists
Lecture 31: Implementing Hash Tables
Lecture 32: Resizing Hash Tables
Lecture 33: Simple Iterators
Lecture 34: List Iterators and Iterator Combinators
Lecture 35: List Iterators and Iterator Combinators
Lecture 36: Zippers
Lecture 37: Naive Tree Iterators
Lecture 38: Efficient Pre-Order Tree Iterators
Lecture 39: Drills
Lecture 40: Drill Solutions
Lecture 41: Wrap-up
6.12

Lecture 4: Classes of Objects: Interface Definitions

Video 2019. Video 2018.

In this lecture, we take an alternative perspective on defining sets of objects; we can characterize objects not just by their construction, as done with a data definition, but also by the methods they support. We call this characterization an interface definition. As we’ll see, designing to interfaces leads to generic and extensible programs.

Let’s take another look at the Light data definition we developed in Lecture 4: Classes of Objects: Interface Definitions. We came up with the following data definition:

;; A Light is one of:
;; - (new red%)
;; - (new green%)
;; - (new yellow%)

We started with a on-tick method that computes the successor for each light. Let’s also add a to-draw method and then build a big-bang animation for a traffic light.

#lang class/0
(require 2htdp/image)
(define LIGHT-RADIUS 20)
 
(define-class red%
  ;; on-tick : -> Light
  ;; Next light after red
  (check-expect (send (new red%) on-tick) (new green%))
  (define (on-tick)
    (new green%))
 
  ;; to-draw : -> Image
  ;; Draw this red light
  (check-expect (send (new red%) to-draw)
                (circle LIGHT-RADIUS "solid" "red"))
  (define (to-draw)
    (circle LIGHT-RADIUS "solid" "red")))
 
(define-class green%
  ;; on-tick : -> Light
  ;; Next light after green
  (check-expect (send (new green%) on-tick) (new yellow%))
  (define (on-tick)
    (new yellow%))
 
  ;; to-draw : -> Image
  ;; Draw this green light
  (check-expect (send (new green%) to-draw)
                (circle LIGHT-RADIUS "solid" "green"))
  (define (to-draw)
    (circle LIGHT-RADIUS "solid" "green")))
 
(define-class yellow%
  ;; on-tick : -> Light
  ;; Next light after yellow
  (check-expect (send (new yellow%) on-tick) (new red%))
  (define (on-tick)
    (new red%))
 
  ;; to-draw : -> Image
  ;; Draw this yellow light
  (check-expect (send (new yellow%) to-draw)
                (circle LIGHT-RADIUS "solid" "yellow"))
  (define (to-draw)
    (circle LIGHT-RADIUS "solid" "yellow")))

We can now create and view lights:

> (send (new green%) to-draw)

image

> (send (new yellow%) to-draw)

image

> (send (new red%) to-draw)

image

To create an animation we can make the following big-bang program:

(require class/universe)
(big-bang (new red%))

At this point, let’s take a step back and ask the question: what is essential to being a light? Our data definition gives us one perspective, which is that for a value to be a light, that value must have been constructed with either (new red%), (new yellow%), or (new green%). But from the world’s perspective, what matters is not how lights are constructed, but rather what can lights compute. All the world does is call methods on the light it contains, namely the on-tick and to-draw methods. We can rest assured that the light object understands the on-tick and to-draw messages because, by definition, a light must be one of (new red%), (new yellow%), or (new green%), and each of these classes defines on-tick and to-draw methods. But it’s possible we could relax the definition of what it means to be a light by just saying what methods an object must implement in order to be considered a light. We can thus take a constructor-agnostic view of objects by defining a set of objects in terms of the methods they understand. We call a set of method signatures (i.e., name, contract, and purpose statement) an interface.

Let’s consider an alternative characterization of lights not in terms of what they are, but rather what they do. Well a light does two things: it can render as an image and it can transition to the next light; hence our interface definition for a light is:

;; A Light implements
;; on-tick : -> Light
;; Next light after this light.
;; to-draw : -> Image
;; Draw this light.

Now it’s clear that each of the three light classes define sets of objects which are Lights, because each implements the methods in the Light interface, but we can imagine new kinds of implementations of the Light interface that are not the light classes we’ve considered so far.

In the next lecture, we’ll explore some consquences of designing in terms of interface definitions intead of data definitions.