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 thanif (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.
- http://www.patternlanguage.com/apl/aplsample/aplsample.htm
- It is useful to describe common problems and common solutions.
- 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;
...
}