Lab 11 - Interfaces

Instructions

Design classes with the given specification in each problem, along with the appropriate test suite. Each method in a class, excluding mutators, accessors, and constructors, must include proper Javadoc comments describing the (1) purpose of the method, (2) parameters, and (3) return value.

Do not round your solutions. For example, do not use Math.round or float your output to, e.g., 2 decimal places, unless specified.

What You Cannot Use

You may not use anything beyond Chapter 4.3. This includes but is not limited to:

Any violation results in a score of 0 on the lab.

Please contact a staff member if you are unsure as to whether you're allowed to use something.

Notes and Version Changes:

Important Warning 1: You must write the method headers for all methods that we list below in order for your code to compile in the autograder. We will not award partial points for non-compiling code. So, we strongly recommend that, before you implement the methods, you first write the method headers and Javadocs.

Problems

  1. (100 points) In this exercise, you'll be developing the IDocument interface along with its implementing classes:

    • TextDocument
    • SpreadsheetDocument
    • PresentationDocument

    The IDocument interface is defined as follows:

    interface IDocument {
    
      /**
       * Returns the number of pages in this document.
       */
      int numberOfPages();
    
      /**
       * Returns a string representing that the Document is being printed.
       */
      default String print() { return "Printing the document!"; }
    }

    Notice that we have a default method, which is one that an implementing class does not have to implement. It provides “default” functionality, should the “implementee” not want to implement the method (hence the name!).

    1. Implement the other three classes with the following specifications:
      • A TextDocument consists of 100 pages. When it is printed, it should return a message "Printing text document!".
      • A SpreadsheetDocument has 50 pages. When it is printed, it should return a message "Printing spreadsheet document!".
      • A PresentationDocument contains 20 pages. It utilizes the default implementation of the print method.
    2. Design the PrintingOffice class, which includes the following static method: static Optional<Double> avgPages(List<IDocument> lodocs). This method calculates and returns the average number of pages across the provided list of IDocument objects. Remember why we use Optional<Double>: if there are no IDocument objects in the list, we would be dividing by zero if we took the average!
    3. Inside the PrintingOffice class, modify it to include the static String printDocuments(List<IDocument> documents) method, responsible for invoking print on each IDocument in the provided list, concatenating the results into a single string, separated by a single comma (i.e., ","), and returning that string.

    Files to Submit: IDocument.java, TextDocument.java, SpreadsheetDocument.java, PresentationDocument.java, PrintingOffice.java, DocumentTest.java

  2. (20 extra credit points) It's often useful to reduce a data structure, e.g., an ArrayList or a BinarySearchTree, down to a single value. Java collections can sometimes do this with streams, but custom data structures such as MiniArrayList/MiniStack do not automatically support this kind of reduction. In Haskell, we can define a way to reduce, or fold, over a type with the Foldable type class. We can model a similar behavior in Java with interfaces.

    1. Design the generic IFoldable<T> interface, which contains one method: <R> R fold(R initial, BiFunction<R, T, R> f).

      Here, T is the element type of the structure implementing IFoldable, R is the type of the accumulated result, initial is the starting accumulator value, and f is the binary function that combines the current accumulator with one element of the structure to produce the next accumulator.

    2. Next, take the MiniArrayList code provided below and modify it to implement IFoldable. You will need to implement the fold method, which should iterate through the elements of the MiniArrayList, applying the function f to each element and the accumulated result, starting with the initial value.

      While we will not require you to test this directly, you should! You can test your implementation by folding over a MiniArrayList of integers to compute their sum, product, etc. This is how we will test your implementation!

      
      class MiniArrayList<T> implements IFoldable<T> {
      
        private static final int DEFAULT_CAPACITY = 10;
        private static final int RESIZE_FACTOR = 2;
        private T[] elements;
        private int size;
         
        MiniArrayList() {
          this(MiniArrayList.DEFAULT_CAPACITY);
        }
      
        MiniArrayList(int capacity) {
          this.size = 0;
          this.elements = (T[]) new Object[capacity];
        }
      
        public <R> R fold(R initial, BiFunction<R, T, R> f) {
          /* TODO you fill this in! */
        }
      
        void add(T element) {
          this.insert(this.size, element);
        }
      
        boolean insert(int idx, T element) {
          if (idx < 0 || idx > this.size) {
            return false;
          }
          if (this.size == this.elements.length) {
            this.resize();
          }
          this.shiftRight(idx);
          this.elements[idx] = element;
          this.size++;
          return true;
        }
      
        Optional<T> get(int idx) {
          return idx < 0 || idx >= this.size ? Optional.empty() : Optional.of(this.elements[idx]);
        }
      
        Optional<T> remove(int idx) {
          if (idx < 0 || idx >= this.size) {
            return Optional.empty();
          } else {
            Optional<T> e = this.get(idx);
            this.shiftLeft(idx);
            this.size--;
            return e;
          }
        }
      
        int size() {
          return this.size;
        }
      
        private void shiftLeft(int idx) {
          for (int i = idx; i < this.size - 1; i++) {
            this.elements[i] = this.elements[i + 1];
          }
        }
      
        private void shiftRight(int idx) {
          for (int i = this.size; i > idx; i--) {
            this.elements[i] = this.elements[i - 1];
          }
        }
      
        private void resize() {
          T[] newElements = (T[]) new Object[this.elements.length * MiniArrayList.RESIZE_FACTOR];
          for (int i = 0; i < this.elements.length; i++) {
            newElements[i] = this.elements[i];
          }
          this.elements = newElements;
        }
      }
      
      

      Note: It does not matter whether you fold left or fold right; we will only test your implementation on associative functions.

    3. Files to Submit: IFoldable.java, MiniArrayList.java