6: Design Patterns

This homework covers design patterns; only the patterns discussed in lecture are used in these questions.
  1. (30 points)
    1. Warm-up: give a definition of what delegation is in object-oriented programming, in your own words.
    2. For each of the patterns discussed in class, answer yes/no on whether any delegation is used in the pattern or not, and briefly justify where for each "yes" case (and if you are not sure feel free to argue a "no" case for partial credit in case you are incorrect):
      1. Observer
      2. Factory Method
      3. Abstract Factory
      4. Command
      5. Singleton
      6. State
      7. Strategy
      8. Decorator
      9. Adapter
      10. Facade
      11. Iterator
      12. Proxy
      13. Template Method
      14. Composite
  2. (30 points)
    1. Warm-up: give a definition of what program to interfaces, not implementations means as an object-oriented design principle, again in your own words.
    2. For each of the patterns discussed in class, answer yes/no on whether the pattern is aligned with this principle by introducing new or better interfaces to program to; briefly justify where for each "yes" case if this principle is used (and if you are not sure feel free to argue a "no" case for partial credit in the case you are incorrect):
      1. Observer
      2. Factory Method
      3. Abstract Factory
      4. Command
      5. Singleton
      6. State
      7. Strategy
      8. Decorator
      9. Adapter
      10. Facade
      11. Iterator
      12. Proxy
      13. Template Method
      14. Composite
  3. (10 points)

    For each of the following situations, indicate the design pattern which should be used. Briefly justify your answer by explaining how the design pattern would be applied.

    1. We have a facade which contains two lists which we shall name List A and List B. We wish to give access to List A to the user of the facade, but we must ensure that no element is added to List A unless it is also in List B.

    2. We are modeling orders for an e-commerce website. An instance of the Order class starts out as a new order; at this time, the order can be changed or cancelled. Once payment is received, the Order can no longer be changed and cancellation applies a restocking fee. Later, the order is shipped and can no longer be cancelled. We wish to keep track of how the order should behave without resorting to a series of if statements in every method.

  4. (15 points)

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

    1. 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?");
          }
          ...
      }
                          
    2. public int performHash(Sample[] samples, int hashMode) {
              int hash = 0;
              for (int i=0;i<samples.length;i++) {
                      switch (hashMode) {
                              case MD5:
                                      hash ^= md5hash(samples[i]);
                                      break;
                              case SHA1:
                                      hash ^= sha1hash(samples[i]);
                                      break;
                              case SHA256:
                                      hash ^= sha256hash(samples[i]);
                                      break;
                              default:
                                      throw new IllegalArgumentException("Invalid hash mode: " + hashMode);
                      }
              }
              return hash;
      }
                          
    3. 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
      }
                          
  5. (15 points)

    The following code makes use of several design patterns. Identify three of them and indicate which lines of code correspond (partially or in whole) to which pattern.

    public class ByteFilteringInputStream extends InputStream {
        /** The listeners to notify when filtering occurs. */
        private Set<FilteringInputStreamListener> listeners = new HashSet<FilteringInputStreamListener>();
        /** The backing input stream from which data is read. */
        private InputStream backer;
        /** The object used to determine if a byte should be filtered. */
        private Filterer filterer;
        
        public ByteFilteringInputStream(InputStream backer, Filterer filterer) {
            this.backer = backer;
            this.filterer = filterer;
        }
        public void addListener(FilteringInputStreamListener listener) {
            this.listeners.add(listener);
        }
        public void removeListener(FilteringInputStreamListener listener) {
            this.listeners.add(listener);
        }
        protected void fireEvent(FilteringInputStreamEvent e) {
            for (FilteringInputStreamListener listener : this.listeners) {
                listener.eventReceived(e);
            }
        }
        public int read() throws IOException {
            int result = this.backer.read();
            boolean filter = this.filterer.shouldFilter(result);
            while (filter) {
                fireEvent(new FilteringInputStreamEvent(result, this.filterer));
                result = this.backer.read();
                filter = this.filterer.shouldFilter(result);
            }
            return result;
        }
    }