teaching machines

CS1: Lecture 38 – Slideshow

December 14, 2019 by . Filed under cs1, fall 2019, lectures.

Dear students,

A primary benefit of object-oriented design is that it helps us organize our programs coherently. We can put code and the data that it regularly processes together into the same chunk. Objects allow for the separation of concerns. Class A can focus on its task, and be very good at it. It can be tested and perfected in isolation. Class B can focus on its task, and be very good at it. If the code for classes A and B were mixed together in class C, the developer’s brain would be pulled in different directions, and its quality would suffer.

So, when we model our problems with objects, we should make cohesive entities. Let’s see a particular occasion where the separation of concerns comes up: when writing an application with a graphical user interface. There are two tasks: to present a view to the user, and to model the data that’s being interacted with.

As it turns out, view code tends to change a lot—we switch operating systems, windowing libraries, and so on. Model code tends to change less frequently. In addition to separating by concerns, we also like to “separate that which changes from that which doesn’t.”

Further yet, by separating the data from its presentation, we can test it more easily. Testing graphical applications is annoying. You have to issue fake mouse clicks, which is cumbersome.

Today, then, let’s write a slideshow application in which concerns have been separated. We’ll start with the model, which knows nothing about presenting data. Once that’s in place, we’ll implement the view, which reads from the model and presents its data.

Let’s follow this checklist:

TODO

Here’s your TODO to complete before we meet again:

See you next class!

Sincerely,

P.S. It’s time for a haiku!

When they run your code
They won’t see the hours you spent
Till they get the bill

P.P.S. Here’s the code we wrote together…

ImageCycler.java

package lecture1213.cs145;

import java.io.File;
import java.util.Arrays;

public class ImageCycler {
  private File[] images;
  private int i;

  public ImageCycler(File directory) {
    images = directory.listFiles();
    Arrays.sort(images);
    i = 0;
  }

  public File getCurrent() {
    return images[i];
  }

  public void forward() {
    i = (i + 1) % images.length;
  }

  public void backward() {
    i = (i - 1 + images.length) % images.length;
  }
}

Main.java

package lecture1213.cs145;

import java.io.File;

public class Main {
  public static void main(String[] args) {
    File directory = new File("/Users/johnch/checkouts/unversioned/images/pets");
    ImageCycler cycler = new ImageCycler(directory);

    new Slideshow(cycler);

//    for (int i = 0; i < 30; ++i) {
//      System.out.println(cycler.getCurrent());
//      cycler.backward();
//    }
  }
}

Slideshow.java

package lecture1213.cs145;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class Slideshow extends JFrame {
  private ImageCycler cycler;
  private JLabel label;

  public Slideshow(ImageCycler cycler) {
    this.cycler = cycler;

    JButton leftButton = new JButton("<");
    JButton rightButton = new JButton(">");

    add(leftButton, BorderLayout.WEST);
    add(rightButton, BorderLayout.EAST);

    label = new JLabel();
    add(label, BorderLayout.CENTER);

    leftButton.addActionListener(e -> {
      cycler.backward();
      loadCurrent();
    });

    rightButton.addActionListener(e -> {
      cycler.forward();
      loadCurrent();
    });

    loadCurrent();
    setSize(1024, 768);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }

  private void loadCurrent() {
    try {
      BufferedImage image = ImageIO.read(cycler.getCurrent());
      label.setIcon(new ImageIcon(image));
    } catch (IOException e) {
      System.err.println(e);
    }
  }
}

ImageCycler.java

package lecture1213.cs148;

import java.io.File;
import java.util.Arrays;

public class ImageCycler {
  private File[] images;
  private int i;

  public ImageCycler(File directory) {
    images = directory.listFiles();
    Arrays.sort(images);
    i = 0;
  }

  public void advance() {
    i = (i + 1) % images.length;
  }

  public void retreat() {
    i = (i - 1 + images.length) % images.length;
  }

  public File getCurrent() {
    return images[i];
  }
}

Main.java

package lecture1213.cs148;

import java.io.File;
import java.util.Arrays;

public class Main {
  public static void main(String[] args) {
    File directory = new File("/Users/johnch/Desktop/pets");
    ImageCycler cycler = new ImageCycler(directory);
    new Slideshow(cycler);

//    for (int i = 0; i < 30; ++i) {
//      System.out.println(cycler.getCurrent());
//      cycler.retreat();
//    }
  }
}

Slideshow.java

package lecture1213.cs148;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class Slideshow extends JFrame {
  private ImageCycler cycler;
  private JLabel label;

  public Slideshow(ImageCycler cycler) {
    this.cycler = cycler;

    JButton leftButton = new JButton("<");
    JButton rightButton = new JButton(">");

    add(leftButton, BorderLayout.WEST);
    add(rightButton, BorderLayout.EAST);

    label = new JLabel();
    add(label, BorderLayout.CENTER);

    leftButton.addActionListener(e -> {
      cycler.retreat();
      loadCurrent();
    });

    rightButton.addActionListener(e -> {
      cycler.advance();
      loadCurrent();
    });

    loadCurrent();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(600, 400);
    setVisible(true);
  }

  private void loadCurrent() {
    try {
      BufferedImage image = ImageIO.read(cycler.getCurrent());
      label.setIcon(new ImageIcon(image));
    } catch (IOException e) {
      System.err.println(e);
    }
  }
}