JMU
Abstract Classes and Interfaces
Design Issues


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
Review (cont.)
Design Issues
Some Examples in Java
A Design Exercise
A Design Exercise (cont.)

An Initial Design

images/madisonmusicmachine-v1.gif
A Design Exercise (cont.)

An Improved Design

images/madisonmusicmachine-v2.gif
A Design Exercise (cont.)

More Improvements

images/madisonmusicmachine-v3.gif
A Design Exercise (cont.)

Eliminating the MadisonMusicMachine Class

images/madisonmusicmachine-v4.gif
A Design Exercise (cont.)

Synchronizing the Music and Lyrics

images/madisonmusicmachine-v5.gif
A Design Exercise (cont.)

Improved Synchronization

images/madisonmusicmachine-v6.gif
A Design Exercise (cont.)

Adding a Presenter Interface

images/madisonmusicmachine-v7.gif
A Design Exercise (cont.)

Combining the Lyrics and Music Classes

images/madisonmusicmachine-v8.gif
A Design Exercise (cont.)

Adding a PlayList Class

images/madisonmusicmachine-v9.gif
A Simple Implementation

AbstractLineElement

javaexamples/oopbasics/musicmachine/AbstractLineElement.java
        /**
 * An element in a line of music
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractLineElement
{
    private int       durationInMillis, wholeNoteInMillis=1500;
    private String    value;
    

    /**
     * Get the duration of this element in milliseconds
     * (at the normal tempo)
     *
     * @return   The duration in milliseconds
     */
    public int getDurationInMillis()
    {
        return durationInMillis;
    }



    /**
     * Get a String representation of the value of this
     *
     * @return   The value
     */
    public String getValue()
    {
       return value;       
    }


    /**
     * Get the duration of this element
     *
     * @param beats  The beats in this element (whole=1, half=2, quarter=4)
     * @param dotted Whether this element is dotted
     */
    protected void setDuration(int beats, boolean dotted)
    {
        durationInMillis = wholeNoteInMillis / beats;
        if (dotted) durationInMillis *= 1.5;
    }
    

    /**
     * Set the String representation of this element
     *
     * @param value  The value
     */
    protected void setValue(String value)
    {
       this.value = value;       
    }
    
    
}
        

Lyric

javaexamples/oopbasics/musicmachine/Lyric.java
        /**
 * A lyric in a song
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Lyric extends AbstractLineElement
{
    
    /**
     * Explicit Value Constructor
     *
     * @param text   The text/word
     * @param beats  The number of beats (whole=1, half=2, quarter=4)
     * @param dotted Dotted or not
     */
    public Lyric(String text, int beats, boolean dotted)
    {
       setValue(text);
       setDuration(beats, dotted);       
    }
    

}
        

Note

javaexamples/oopbasics/musicmachine/Note.java
        /**
 * A note in a song
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Note extends AbstractLineElement
{
    private boolean   sharp;
    private char      pitch;


    /**
     * Default Constructor
     *
     * Constructs a whole note at middle C
     */
    public Note()
    {
        this('C', false, 0, 1, false);
    }




    /**
     * Explicit Value Constructor
     *
     * @param pitch    The pitch ('A','B','C','D','E','F', 'G' or 'R')
     * @param sharp    true for a sharp and false for a natural
     * @param octave   The octave (relative to middle C)
     * @param duration 1 for whole notes, 2 for half notes, etc...
     * @param dotted   Whether the note is dotted
     */
    public Note(char pitch,   boolean sharp, int octave, 
                int duration, boolean dotted)
    {
        int     durationInMillis, midiBase, midiNumber;

        // Store the pitch and duration
        this.pitch    = Character.toUpperCase(pitch);
        
        // Correct for possible "mistakes" (B# and E#)
        // (Alternatively, we could treat a B# as a C and an
        // E# as an F)
        if ((pitch == 'B') || (pitch == 'E')) this.sharp = false;
        else                                  this.sharp = sharp;

        // Calculate the MIDI value
        midiBase = 60;
        if      (pitch == 'A') midiNumber = midiBase +  9;
        else if (pitch == 'B') midiNumber = midiBase + 11;
        else if (pitch == 'C') midiNumber = midiBase +  0;
        else if (pitch == 'D') midiNumber = midiBase +  2;
        else if (pitch == 'E') midiNumber = midiBase +  4;
        else if (pitch == 'F') midiNumber = midiBase +  5;
        else if (pitch == 'G') midiNumber = midiBase +  7;
        else                   midiNumber = -1;             // Rest        

        if (this.sharp) midiNumber = midiNumber + 1;

        midiNumber = midiNumber + (octave * 12);

        setValue(""+midiNumber);
        setDuration(duration, dotted);        
    }

}
        
A Simple Implementation (cont.)

Presenter

javaexamples/oopbasics/musicmachine/Presenter.java
        /**
 * Requirements of an object that can display the elements
 * in a Part
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface Presenter
{
    /**
     * Set the value to be used the next time this Presnter
     * is turned on 
     *
     * @param value   The value
     */
    public abstract void setValue(String value);
    

    /**
     * Turn this presenter off
     */
    public abstract void turnOff();
    

    /**
     * Turn this presenter on
     */
    public abstract void turnOn();
    


}
        

Console

javaexamples/oopbasics/musicmachine/Console.java
        /**
 * An object that displays text on the console
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Console implements Presenter
{
    private String       text;
    

    /**
     * Set the value to be used the next time this Presnter
     * is turned on (required by Presenter)
     *
     * In this case, it is the text to display
     *
     * @param value   The value
     */
    public void setValue(String value)
    {
       text = value;
       
    }
    
    
    /**
     * Turn this presenter off (required by Presenter)
     *
     * In this case, this method does nothing
     */
    public void turnOff()
    {
    }
    
    

    /**
     * Turn this presenter on (required by Presenter)
     *
     * In this case, this method outputs the value to
     * the console
     */
    public void turnOn()
    {
       System.out.println(text);
    }
    
    


}
        

InstrumentSynthesizer

javaexamples/oopbasics/musicmachine/InstrumentSynthesizer.java
        import javax.sound.midi.*;


/**
 * An object that synthesizes the sounds made by an instrument
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class InstrumentSynthesizer implements Presenter
{
    int          midiNote;
    MidiChannel  channel;
    

    /**
     * Explicit Value Constructor
     *
     * @param channel   The MIDI channel on the synthesizer to use
     */
    public InstrumentSynthesizer(MidiChannel channel)
    {
       this.channel = channel;       
    }
    


    /**
     * Set the value to be used the next time this Presnter
     * is turned on (required by Presenter)
     *
     * In this case, it is a String representation of the
     * MIDI note value
     *
     * @param value   The value
     */
    public void setValue(String value)
    {
       midiNote = Integer.parseInt(value);
       
    }
    
    

    /**
     * Turn this presenter off (required by Presenter)
     *
     * In this case, this method turns the MIDI channel
     * off
     */
    public void turnOff()
    {
       channel.noteOff(midiNote);       
    }
    
    

    /**
     * Turn this presenter on (required by Presenter)
     *
     * In this case, this method turns the MIDI channel
     * on
     */
    public void turnOn()
    {
       channel.noteOn(midiNote, 127);       
    }
    
    


}
        
A Simple Implementation (cont.)

MetronomeListener

javaexamples/oopbasics/musicmachine/MetronomeListener.java
        /**
 * Requirements of a MetronomeListener
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface MetronomeListener
{
    /**
     * Handle a "tick" of the Metronome
     *
     * @param metronome  The source of the "tick"
     */
    public abstract void handleTick(Metronome metronome);
}
        
A Simple Implementation (cont.)

Part

javaexamples/oopbasics/musicmachine/Part.java
        import java.awt.event.*;
import java.util.*;

/**
 * A Part (i.e., a sequence of Note or Lyric objects) in a Score
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Part  implements MetronomeListener
{
    private AbstractLineElement      element;
    private AbstractLineElement[]    elements;
    private int                      currentIndex, duration;
    private int                      elapsed, lastIndex;    
    private Presenter                presenter;


    /**
     * Explicit Value Constructor
     *
     * @param size  The number of elements in this Part
     */
    public Part(int size)
    {
       elements     = new AbstractLineElement[size];
       currentIndex = 0;       
       lastIndex    = 0;       
       duration     = 0;       

    }




    /**
     * Get an AbstractLineElement
     */
    public AbstractLineElement getElement(int i)
    {
       AbstractLineElement      element;
       
       element = null;
       if ((i >= 0) && (i < elements.length))
       {
          element = elements[i];          
       }
       

       return element;       
    }



    /**
     * Handle a "tick" of the Metronome (required by MetronomeListener)
     *
     * @param metronome  The source of the "tick"
     */
    public void handleTick(Metronome metronome)
    {
       elapsed += metronome.DELAY_IN_MILLIS;
       
       if (elapsed >= duration)
       {
          presenter.turnOff();
          
          element = getElement(currentIndex);
          currentIndex++;          

          if (element != null)
          {
             duration = element.getDurationInMillis();

             presenter.setValue(element.getValue());
             presenter.turnOn();
          }
          
          elapsed = 0;          
       }
    }
    




    /**
     * Set an AbstractLineElement
     *
     * @param i        The index
     * @param element  The element
     */
    public void setElement(int i, AbstractLineElement element)
    {
       if ((i >= 0) && (i < elements.length))
       {
          elements[i] = element;
       }
    }



    /**
     * Set the Presenter to use when "presenting" this
     * Part
     *
     * @param presenter   The Presenter to use
     */
    public void setPresenter(Presenter presenter)
    {
       this.presenter = presenter;
       
    }
    
}
        
A Simple Implementation (cont.)

Song

javaexamples/oopbasics/musicmachine/Song.java
        import java.awt.event.*;

/**
 * A Song
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Song implements MetronomeListener
{
    
    private Part[]           parts;
    private String           title;
    

    /**
     * Explicit Value Constructor
     *
     * @param numberOfParts  The number of Part objects in this Song
     */
    public Song(int numberOfParts)
    {
       parts = new Part[numberOfParts];       
    }
    

    /**
     * Set a Part
     *
     * @param i     The index
     * @param part  The Part
     */
    public void setPart(int i, Part part)
    {
       parts[i] = part;       
    }
    


    /**
     * Handle a "tick" of the Metronome (required by MetronomeListener)
     *
     * In this case, this method just delegates to the
     * component Part objects
     *
     * @param metronome  The source of the "tick"
     */
    public void handleTick(Metronome metronome)
    {
       for (int i=0; i<parts.length; i++)
       {
          if (parts[i] != null) parts[i].handleTick(metronome);          
       }
    }
    
}
        
Concluding Thoughts