diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ComparatorStrategy.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ComparatorStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..190b583645f973150ecf412fd93d4b7862195c66
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ComparatorStrategy.java
@@ -0,0 +1,74 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Function;
+
+public class ComparatorStrategy {
+
+  public static void main(String[] args) {
+    List<Integer> values = Arrays.asList(3, 5, 7, 2, 1, 1, 2, 7, 8, 8, 9);
+    Comparator<Integer> strategy =
+        Comparator.<Integer>comparingInt(x -> x % 2).thenComparing(Function.identity()).reversed();
+    Collections.sort(values, strategy);
+    System.out.println(values);
+
+    removeDuplicates(List.of("a", "A", "b"), String::equalsIgnoreCase);
+    removeDuplicates(List.of("a", "A", "b"), String::equals);
+  }
+
+  interface EqualsTest {
+    boolean isEqual(String s, String t);
+  }
+
+  private static List<String> removeDuplicates(List<String> input, EqualsTest test) {
+    ArrayList<String> result = new ArrayList<>();
+    for (String s : input) {
+      boolean found = false;
+      for (String r : result) {
+        if (test.isEqual(s, r)) {
+          found = true;
+        }
+      }
+      if (!found) {
+        result.add(s);
+      }
+    }
+    return result;
+  }
+
+  private static List<String> removeDuplicatesIgnoringCase(List<String> input) {
+    ArrayList<String> result = new ArrayList<>();
+    for (String s : input) {
+      boolean found = false;
+      for (String r : result) {
+        if (s.equalsIgnoreCase(r)) {
+          found = true;
+        }
+      }
+      if (!found) {
+        result.add(s);
+      }
+    }
+    return result;
+  }
+
+  private static List<String> removeDuplicatesExactMatch(List<String> input) {
+    ArrayList<String> result = new ArrayList<>();
+    for (String s : input) {
+      boolean found = false;
+      for (String r : result) {
+        if (s.equals(r)) {
+          found = true;
+        }
+      }
+      if (!found) {
+        result.add(s);
+      }
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/DvdCompositePattern.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/DvdCompositePattern.java
new file mode 100644
index 0000000000000000000000000000000000000000..eafa0f928717b902eb0afd7a83c1d87084f31fef
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/DvdCompositePattern.java
@@ -0,0 +1,78 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+interface Watchable {
+  int priceInPence();
+
+  String title();
+}
+
+class Dvd implements Watchable {
+  private final String title;
+  private final int priceInPence;
+
+  Dvd(String title, int priceInPence) {
+    this.title = title;
+    this.priceInPence = priceInPence;
+  }
+
+  @Override
+  public int priceInPence() {
+    return priceInPence;
+  }
+
+  @Override
+  public String title() {
+    return title;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%d: %s%n", priceInPence(), title());
+  }
+}
+
+class BoxSet implements Watchable {
+
+  private final List<Watchable> items;
+
+  BoxSet(List<Watchable> items) {
+    this.items = List.copyOf(items);
+  }
+
+  BoxSet(Watchable... items) {
+    this.items = List.copyOf(Arrays.asList(items));
+  }
+
+  @Override
+  public int priceInPence() {
+    return (int) (items.stream().mapToInt(Watchable::priceInPence).sum() * 0.9);
+  }
+
+  @Override
+  public String title() {
+    return items.stream().map(Watchable::title).collect(Collectors.joining(", "));
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%d: %s%n", priceInPence(), title());
+  }
+}
+
+public class DvdCompositePattern {
+  public static void main(String[] args) {
+    Dvd m1 = new Dvd("Episode IV: A New Hope", 100);
+    Dvd m2 = new Dvd("Episode V: The Empire Strikes Back", 100);
+    Dvd m3 = new Dvd("Episode VI: Return of the Jedi", 100);
+    BoxSet b1 = new BoxSet(m1, m2, m3);
+    Dvd m4 = new Dvd("Episode I: The Phantom Menace", 5);
+    Dvd m5 = new Dvd("Episode II: Attack of the Clones", 5);
+    Dvd m6 = new Dvd("Episode III: Revenge of the Sith", 5);
+    BoxSet b2 = new BoxSet(m4, m5, m6);
+    BoxSet all = new BoxSet(b1, b2);
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/Factory.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/Factory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ad00b3990a653496a34561da4595f65b7e573fb
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/Factory.java
@@ -0,0 +1,44 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Factory {
+
+  private Map<String, Integer> used = new HashMap<>();
+  private int counter = 0;
+
+  static class SpecialThing {
+
+    private final String name;
+    private final int id;
+
+    private SpecialThing(String name, int id) {
+      this.name = name;
+      this.id = id;
+    }
+  }
+
+  class AnotherOne {
+
+    AnotherOne() {
+      counter++;
+    }
+  }
+
+  public SpecialThing newSpecialThing(String name) {
+    if (!used.containsKey(name)) {
+      used.put(name, counter++);
+    }
+    return new SpecialThing(name, used.get(name));
+  }
+
+  public static void main(String[] args) {
+
+    Factory factory1 = new Factory();
+    Factory factory2 = new Factory();
+
+    SpecialThing one = factory1.newSpecialThing("apple");
+    SpecialThing two = factory2.newSpecialThing("orange");
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithEnum.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithEnum.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e8d3aa3c8e4747a68ed449f18c8e8d2193ae527
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithEnum.java
@@ -0,0 +1,51 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+/**
+ * Model a desk fan with three settings: off, slow and fast.
+ *
+ * <p>The fan has one button to click through these three settings.
+ *
+ * <p>The two methods on this class are called by the firmware of the fan. {@code click} is called
+ * whenever someone presses the button to change setting. {@code update} is called whenever the
+ * motor is ready to change speed.
+ */
+public class FanSpeedWithEnum {
+
+  enum Speed {
+    OFF,
+    SLOW,
+    FAST
+  }
+
+  private Speed state;
+
+  /** Set the motor turning at the correct speed. */
+  void update(MotorController motorController) {
+    switch (state) {
+      case OFF:
+        motorController.stop();
+        break;
+      case SLOW:
+        motorController.turnSlow();
+        break;
+      case FAST:
+        motorController.turnFast();
+        break;
+    }
+  }
+
+  /** Respond to a button click to change between settings. */
+  void click() {
+    switch (state){
+      case OFF:
+        state = Speed.SLOW;
+        break;
+      case SLOW:
+        state = Speed.FAST;
+        break;
+      case FAST:
+        state = Speed.OFF;
+        break;
+    }
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithInts.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithInts.java
new file mode 100644
index 0000000000000000000000000000000000000000..8439bef8a64156d165705f0840091d58e73c2e66
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithInts.java
@@ -0,0 +1,47 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+/**
+ * Model a desk fan with three settings: off, slow and fast.
+ *
+ * <p>The fan has one button to click through these three settings.
+ *
+ * <p>The two methods on this class are called by the firmware of the fan. {@code click} is called
+ * whenever someone presses the button to change setting. {@code update} is called whenever the
+ * motor is ready to change speed.
+ */
+public class FanSpeedWithInts {
+
+  private static final int OFF = 0;
+  private static final int SLOW = 1;
+  private static final int FAST = 2;
+
+  private int state = OFF;
+
+  /** Set the motor turning at the correct speed. */
+  void update(MotorController motorController) {
+    switch (state) {
+      case OFF:
+        motorController.stop();
+        return;
+      case SLOW:
+        motorController.turnSlow();
+        return;
+      case FAST:
+        motorController.turnFast();
+    }
+  }
+
+  /** Respond to a button click to change between settings. */
+  void click() {
+    switch (state) {
+      case OFF:
+        state = SLOW;
+        return;
+      case SLOW:
+        state = FAST;
+        return;
+      case FAST:
+        state = OFF;
+    }
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithStatePattern.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithStatePattern.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae4d5d2b6ea45725cf0f78269a9c3e24c2c7d6d8
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/FanSpeedWithStatePattern.java
@@ -0,0 +1,69 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+/**
+ * Model a desk fan with three settings: off, slow and fast.
+ *
+ * <p>The fan has one button to click through these three settings.
+ *
+ * <p>The two methods on this class are called by the firmware of the fan. {@code click} is called
+ * whenever someone presses the button to change setting. {@code update} is called whenever the
+ * motor is ready to change speed.
+ */
+public class FanSpeedWithStatePattern {
+
+  interface FanState {
+    void update(MotorController motorController);
+
+    void click();
+  }
+
+  class Stop implements FanState {
+    @Override
+    public void update(MotorController motorController) {
+      motorController.stop();
+    }
+
+    @Override
+    public void click() {
+      state = new Slow();
+    }
+  }
+
+  class Slow implements FanState {
+
+    @Override
+    public void update(MotorController motorController) {
+      motorController.turnSlow();
+    }
+
+    @Override
+    public void click() {
+      state = new Fast();
+    }
+  }
+
+  class Fast implements FanState {
+
+    @Override
+    public void update(MotorController motorController) {
+      motorController.turnFast();
+    }
+
+    @Override
+    public void click() {
+      state = new Stop();
+    }
+  }
+
+  private FanState state = new Stop();
+
+  /** Set the motor turning at the correct speed. */
+  void update(MotorController motorController) {
+    state.update(motorController);
+  }
+
+  /** Respond to a button click to change between settings. */
+  void click() {
+    state.click();
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/LambdaRef.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/LambdaRef.java
new file mode 100644
index 0000000000000000000000000000000000000000..96f2da246daa06c6df2c047ce019f2b92c0a4438
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/LambdaRef.java
@@ -0,0 +1,36 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+class Doubler {
+
+  int dbl(int x) {
+    return x * 2;
+  }
+}
+
+class StaticOrNot {
+
+  int x;
+
+  int dbl() {
+    return this.x * 2;
+  }
+
+  static int dbl2(StaticOrNot thiss) {
+    return thiss.x * 2;
+  }
+}
+
+public class LambdaRef {
+
+  public static void main(String[] args) {
+    List<Integer> ints = List.of(1, 2, 3, 4, 5, 6);
+    Doubler dd = new Doubler();
+    List<Integer> doubled = ints.stream().map(dd::dbl).collect(Collectors.toList());
+
+    List<String> strings = List.of("andy", "is", "here");
+    List<String> upper = strings.stream().map(String::toUpperCase).collect(Collectors.toList());
+  }
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/MotorController.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/MotorController.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbc5dcc1eeea251d7a826228598bbcf691595944
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/MotorController.java
@@ -0,0 +1,10 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+class MotorController {
+
+  void turnFast() {}
+
+  void turnSlow() {}
+
+  void stop() {}
+}
diff --git a/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ObserverPattern.java b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ObserverPattern.java
new file mode 100644
index 0000000000000000000000000000000000000000..10111083de8619e9a641d50b8fab54396f9172c6
--- /dev/null
+++ b/src/main/java/uk/ac/cam/acr31/oop/democode1920/lecture12/ObserverPattern.java
@@ -0,0 +1,43 @@
+package uk.ac.cam.acr31.oop.democode1920.lecture12;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Font;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+public class ObserverPattern {
+
+  public static void main(String[] args) {
+
+    JFrame window = new JFrame();
+    window.setSize(500, 500);
+    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+    window.setTitle("A very exciting GUI");
+
+    JLabel label = new JLabel("Hello", JLabel.CENTER);
+    window.add(label, BorderLayout.CENTER);
+    label.setForeground(Color.RED);
+    label.setFont(new Font(label.getFont().getFontName(), Font.BOLD, 30));
+
+    JButton button = new JButton("Click me! Click me!");
+    window.add(button, BorderLayout.SOUTH);
+
+    // Important bit - you can do this as a lambda or an anonymous inner class or indeed anything
+    // that implements ActionListener
+    button.addActionListener(
+        e -> {
+          if (label.getText().equals("Hello")) {
+            label.setText("Goodbye");
+          } else {
+            label.setText("Hello");
+          }
+        });
+
+    // you can have as many as you like
+    button.addActionListener(e -> System.out.println("CLICKED!"));
+
+    window.setVisible(true);
+  }
+}