Software Engineering

Written Assignment 4: Design Patterns

Due on November 29th at 11:59 PM.

This is an individual assignment. You are to complete this assignment on your own, although you may discuss the lab concepts with your classmates. Remember the Academic Integrity Policy: do not show your work to anyone outside of the course staff and do not look at anyone else’s work for this lab. If you need help, please post on the Piazza forum or contact the instructor. Note that no exception is made for your group members; collaboration with your group members is a violation of the Academic Integrity Policy as much as collaboration with other members of the class. If you have any doubts about what is okay and what is not, it’s much safer to ask than to risk violating the Academic Integrity Policy.

Your Task

For this assignment, write answers to the questions below and upload them to your personal GitHub repository for the assignment. You can find your repository at:

git@github.swarthmore.edu:cs71-f17/written4-<username>.git

where <username> should be replaced by your Swarthmore username. You may add whatever materials to this repository you please, but your answers to each question must be clearly identified.

Design Patterns

This assignment requires you to demonstrate understand of design patterns. You’ll want to keep the course’s Design Patterns Guide handy. Only design patterns from that guide are necessary to complete this assignment.

Part I: Identifying Patterns

For each of the following code snippets, identify the design pattern best represented by that code. Briefly explain your reasoning.

  1. public class Employee {
        ...
    }
    public class Manager extends Employee {
        private List<Employee> directSubordinates;
        public Manager(List<Employee> directSubordinates) {
            this.directSubordinates = directSubordinates;
        }
        public List<Employee> getDirectSubordinates() {
            return this.directSubordinates;
        }
    }
    
  2. public class TreeWalker<T> {
        private List<TreeNode<T>> remainingNodes;
        public TreeWalker(TreeNode<T> root) {
            this.remainingNodes = new LinkedList<TreeNode<T>>();
            this.remainingNodes.add(root);
        }
        public boolean finishedWalking() {
            return this.remainingNodes.isEmpty();
        }
        public T getNextNodeData() {
            TreeNode<T> node = this.remainingNodes.remove(0);
            if (node.getLeft() != null) {
              this.remainingNodes.add(node.getLeft());
            }
            if (node.getRight() != null) {
              this.remainingNodes.add(node.getRight());
            }
            return node.getData();
        }
    }
    
  3. public interface Combiner<T> {
         public T combine(T a, T b);
    }
    ...
    public T accumulate(List<T> items, T baseValue, Combiner<T> combiner) {
        T accumulator = baseValue;
        for (T t : items) {
            accumulator = combiner.combine(accumulator, t);
        }
        return accumulator;
    }
    

Part II: Correcting Problems

For each of the following code snippets, identify which design pattern should have been used to improve the quality of the code. Briefly explain why that design pattern would’ve led to better code.

  1. public void startWork() {
        worker.setJob(someJob);
        Thread finishCleanupThread = new Thread(new Runnable() {
            public void run() {
                while (worker.isWorking()) { }
                worker.cleanup();
            }
        });
        finishCleanupThread.start();
        worker.start(); // non-blocking call which starts work
    }
    
  2. public class FitnessCustomer {
        private static enum Level {
            BRONZE, SILVER, GOLD
        }
        private Level level;
        public void setLevel(Level level) { this.level = level; }
        public double getFees() {
            switch (level) {
                case BRONZE: return CustomerConstants.BRONZE_FEES;
                case SILVER: return CustomerConstants.SILVER_FEES;
                case GOLD: return CustomerConstants.GOLD_FEES;
            }
            throw new IllegalStateException("How did I get here?");
        }
        public boolean canAccessPool() {
            return (level == Level.SILVER || level == Level.GOLD);
        }
        public boolean hasOwnLocker() {
            return (level == Level.GOLD);
        }
        public double getEquipmentDiscount() {
            switch (level) {
                case BRONZE: return CustomerConstants.BRONZE_DISCOUNT;
                case SILVER: return CustomerConstants.SILVER_DISCOUNT;
                case GOLD: return CustomerConstants.GOLD_DISCOUNT;
            }
            throw new IllegalStateException("How did I get here?");
        }
        ...
    }
    

Part III: API Examples

Examine the following members of the Java API. For each one, identify the design pattern that the member represents and briefly explain why.

  1. The AbstractList.get(int) method.

  2. The PushbackReader class.

  3. Objects returned by the Collections.unmodifiableSet method.

Part IV: Design Scenarios

Consider each of the scenarios below and identify the design pattern which is most directly addresses the problem described. Briefly explain your reasoning.

  1. You’ve developed a new implementation of the List interface and you’d like to test the behavior of your new data structure. You’ve written an algorithm to perform your speed tests, but the algorithm needs to make a great many instances of your list class. You want to test the performance of your list against the performance of ArrayList and LinkedList, but you don’t want to have to write your algorithm three times in order for it to be able to create the right kind of list to test.

  2. You’ve completed a compiler for a new language! There are many parts to your compilation process: parsing, transformation, assembly code generation, and so forth. You’d like to allow other programmers to use an interface to compile their code without resorting to system calls or other command-line invocations – your compiler can just run in their processes – but you don’t want those users to have to know how to bring all of the steps of compilation together in order to use your compiler.