Skip to main content

CSC 321.01, Class 19: Design patterns

Overview

  • Preliminaries
    • Notes and news
    • Upcoming work
    • Extra credit
    • Questions
  • Why design patterns?
  • Flyweight
  • Iterator
  • Adaptor / Decorator / Wrapper

Preliminaries

News and notes

  • Cookies!

Upcoming work

  • HW9: Document a Design Pattern due Tuesday at 9:00 p.m.
    • Goal: Clear explanation, with examples. I will have you talk about your example in class.
    • Factory (any kind is fine): FA, SG, JM, JZ
    • Observer: MB, MG, AM, AM
    • Proxy: EB, BH, DN, MD
    • Strategy: GC, PK, JS, JRL
    • Template Method: WSC, NA, RS

Good things to do (Academic/Artistic)

  • Faulconer gallery has two great exhibits.
  • Tuesdsay Extra

Good things to do (Other)

Questions

Why design patterns?

  • Much of the work we do involves discovering and using patterns.
  • E.g., to do something to each element of a list in Scheme, you might write
(define proc
  (lambda (lst)
    (if (null? lst)
        null
        (cons (something (car lst)) 
              (proc (cdr lst))))))
  • In some sense, these are just techniques for approaching problems.
  • Here’s a pattern for writing safer comparisons in C.
    • if (CONST == VAR) { ... } is better than if (VAR == CONST) { ... }
    • if (5 == x) { ... }
    • if (x = 5) { ... }
  • About three decades ago, Christopher Alexander wrote a book entitled “A Pattern Language” about the design of houses.
  • About two and a half decades ago, a group of OO software designers thought that a similar approach for OO software design.
    • Describe problem domains and the way experts tend to approach them, so that novices can learn to write better architected systems
  • “Design Patterns” - The name they came up for this idea.
  • Multiple approaches to the traditional design patterns
    • Intended as “Here’s a way to think about the problem.”
    • Also works as a way of discussing a solution (or the design of a system).
    • Unfortunately, sometimes treated as an absolute.
      • Which leads some to discard them.
    • Many languages now incorporate design patterns in the design of the language. For example, Java has iterators. Rails has MVC.
  • A good design pattern usually has at least four parts. (Yes, I’ll do examples.)
    • The problem that the pattern addresses.
    • The “good”/recommend/experienced solution and/or philosophy of how to approach the problem
    • Examples, which ground the idea
    • A diagram which shows the relationship between the classes.

Flyweight

Problem: We have expensive/large/etc. state data. We have lots of objects.

Solution: As much as possible, decouple the state from each object.

Example: Typography (or even just getting letters on the screen)

  • We will be drawing sequences of characters on the screen.
  • So we have something like the following
class Line {
  Character[] contents;
}

class Character {
  int x;
  int y;
  BigBunchOfData thisCharacterAt12pt;
  BigBunchOfData thisCharacterAt14pt;
  BigBunchOfData thisCharacterAt16pt;
  BigBunchOfData thisCharacterAt18pt;
  BigBunchOfData thisCharacterAt22pt;

  public drawYourself(Screen screen) {
    ...
  }
}

We can move the instructions for drawing the character outside of the character itself.

class Character {
  int x;
  int y;
  CharacterData data;
  public drawYourself(Screen screen) {
    data.drawYourselfAt(screen, x, y);
  }
}
class CharacterData {
  BigBunchOfData thisCharacterAt12pt;
  BigBunchOfData thisCharacterAt14pt;
  BigBunchOfData thisCharacterAt16pt;
  BigBunchOfData thisCharacterAt18pt;
  BigBunchOfData thisCharacterAt22pt;
  
  public drawYourSelfAt(Screen screen, int x, int y) {
    ...
  } 
} 

We’ve traded an extra procedure call for a big savings in storage.

Construction is now a bit harder.

It used to be: Make a new Character. Load the character data for that character from wherever. Now it’s _Make a new object. Find the associated heavyweight object. Link to it.

Iterator

Problem: I want to do so something to each element of a collection.

Traditional bad approach: Each collection provides its own mechanism for dealing with iteration.

for (i = 0; i < A.size; i++) {
  doSomethingTo(A[i]);
}

tmp = L.front;
while (tmp != null) {
  doSomethingTo(tmp.val);
  tmp = tmp.next;
}

Meta problem: The code looks different.

  • Cognitive overload: You’re doing the same thing, why does it look different? Code for similar goals should look the same.
  • Substitution is not easy.
  • No encapsulation

Philosophy/Approach: Rather than having a custom approach for each collection, have collection return an object that allows the consumer to iterate the values.

Note: Designing the Iterator interface is hard and may be context dependent.

public interface Iterator<T> {
  T current();
  void next();
  boolean hasNext();
}

public interface Iterator<T> {
  T getNext() throws NoMoreElementsException;
}

Diagram:

Adaptor

Problem: I need X. I have a Y. Y is a lot like X.

Example: I need a first-in first-out to-do list. I have a Queue. How do I use my Queue as a to-do list?

public class Queue {
  public void enqueue(String val);
  public String dequeue();
  public boolean empty();
}

public interface ToDoList {
  public void addTask(String task);
  public String nextTask();
  public boolean tasksRemain();
}

public class HenrysAwesomeLifeManager {
  ToDoList list;
  ...
}