teaching machines

CS 1: Lecture 33 – Separation of Concerns in Slideshow

December 1, 2017 by . Filed under cs1, cs145, cs148, fall 2017, 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:

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

See you next class!

Sincerely,

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

Who moves the clock’s hands?
It does, thank goodness it does
I don’t have the time

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

ImageCycler.java

package lecture1201;

import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

public class ImageCycler {
  private File[] images;
  private int index;
  
  public ImageCycler(File directory) {
    images = directory.listFiles();
    index = 0;
  }
  
  public void previous() {
    index = (index + images.length - 1) % images.length;
  }
  
  public void next() {
    index = (index + 1) % images.length;
  }
  
  public ImageIcon getCurrent() {
    try {
      return new ImageIcon(ImageIO.read(images[index]));
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
}

Slideshow.java

package lecture1201;

import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Slideshow extends JFrame {
  public Slideshow(ImageCycler cycler) {
    JLabel label = new JLabel("foobag");
    JButton previousButton = new JButton("<");
    JButton nextButton = new JButton(">");

    add(previousButton, BorderLayout.WEST);
    add(nextButton, BorderLayout.EAST);
    add(label, BorderLayout.CENTER);
    
    previousButton.addActionListener(e -> {
      cycler.previous();
      label.setIcon(cycler.getCurrent());
    });
    
    nextButton.addActionListener(e -> {
      cycler.next();
      label.setIcon(cycler.getCurrent());
    });
    
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
  }
}

Main.java

package lecture1201;

import java.io.File;

public class Main {
  public static void main(String[] args) {
    ImageCycler cycler = new ImageCycler(new File("/Users/johnch/checkouts/unversioned/images/pets"));
//    for (int i = 0; i < 10; ++i) {
//      cycler.previous();
//      System.out.println(cycler.getCurrent());
//    }
    Slideshow slideshow = new Slideshow(cycler);
  }
}