teaching machines

CS 1: Lecture 33 – Separation of Concerns in Slideshow

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:

  • Create an ImageCycler class that gathers up all the images in a directory.
  • Have the cycler focus on a single image in the sequence.
  • Allow the cycler to advance and retreat through the sequence.
  • Imbue the cycler with the ability to yield the currently selected File.
  • Create a window that accepts an ImageCycler.
  • Give it left and right buttons.
  • Add listeners for button clicks.
  • Give it a label that displays the name of the currently selected image.
  • Instead of the file name, display the image.

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

  • CS 148, your last lab is posted. I advise you to complete it before lab begins, as there will be no next lab. You must also beat a couple of my SplatBot implementations before lab is over.

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);
  }
}

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *