Skip to main content

CSC 321.01, Class 18: Design patterns

Overview

  • Preliminaries
    • Notes and news
    • Upcoming work
    • Friday PSA
    • Questions
  • Why design patterns?
  • Reading reactions
  • Flyweight
  • Iterator
  • Observer
  • Adaptor
  • Factory

News / Etc.

  • Food-like substances!

Upcoming work

  • For Monday: Write a thorough description of one design pattern (as assigned by Sam).

Good things to do

  • Lots of things in the Rosenfield Symposium on Technology and Human Rights.
  • CS Table, Tuesday at noon, Essays and Poetry
  • CS Extras, Thursday at 4:15 pm, The CS Curriculum

Friday PSA

  • I brought you BAC cards
  • Be responsible
  • Get enough sleep!

Questions

Why design patterns?

  • Goal: Design better software
  • As you design, you learn, you get better at what you do.
  • Originally, express these lessons as “principles”
    • Do this: In tests, use CONSTANT == VARIABLE rather than VARIABLE == CONSTANT if (2 == x) ... rather than if (x == 2) .... (A good habit for catching the occasional “whoops, I only wrote one =”.
    • Don’t do that: “GOTO considered harmful”
    • Think about this: Polymorphism
  • In the world of architecture, there is a somewhat influential book called “A Pattern Language” by Christopher Alexander.
    • Goal
    • Approach
    • Example
  • Applies to software, too. “In this situation, …”
  • When building a large software system with a data store and a user interface, it is helpful to separate it into three systems with a well documented set of rules for communicating between those systems. (MVC)
  • Many people love patterns, and use them in many situations
    • CS Education Patterns
    • Functional patterns
    • DEVops patterns
  • Some people hate! patterns
    • Can make programming feel like following boilerplate
    • Some people shoehorn problems into patterns
    • Limits freedom of thought
  • Most people are somewhere in the middle
    • Good things to know about because they can help you think about solutions and can reveal new approaches
    • Provide a vocabulary for talking to other programmers
  • You will benefit from knowing some of the vocabulary and some of the things these patterns reveal

Reading reactions

What new design principle or pattern did you find most useful?

  • Adapter, Proxy, Facade, Bridge
  • Builder
  • Command
  • Dependency injection
  • Interpreter
  • Liskov substitution [x3]
  • Observer
  • Open/closed [x2]
  • Proxy
  • Sequence diagram
  • Single responsibility principle [x5]
  • Template method

Which design principle or pattern did you find the most relevant to your prior experience with object-oriented programming? Explain the pattern and why you chose it.

  • Iterator [x3]
  • Data clump
  • Decorator
  • Demeter [x2]
  • Dependency injection
  • Liskov substitution [x4]
  • Observer
  • Open/closed [x2]
  • Single responsibility principle [x4]
  • State [x2]

Most confusing

  • Adapter, Proxy, Facade, Bridge
  • Chain of responsibility
  • Composite
  • Decorator [x3]
  • Delegation
  • Demeter principle
  • Dependency injection [x4]
    • We’ll talk about it on Monday
  • Interface segregation
  • Liskov substitution [x3]
    • Subtype polymorphism
  • Open/Closed [x2]
  • Single responsibility [x2]
  • Strategy
  • Template method

Flyweight

Problem: Some programs with lots of objects want to treat the objects as separate, even though the objects are very similar.

Philosophy: Find a way to share data.

Example: In setting type digitally, I have either a grid of pixels or a set of curves to represent each character. “[A]lphabeta” Both “a”s have the same pattern, so there’s no reason to store the pattern in the character. The class Character should be a flyweight.

class CharacterShape
{
  BigChunkOfData 8pt;
  BigChunkOfData 10pt;
  BigChunkOfData 12pt;
  BigChunkOfData 14pt;
  BigChunkOfData 16pt;

  void render(int x, int y, int size)
  {
    ...
  }
}

class Character
{
  CharacterShape shape;
  void render(int x, int y, int size)
  {
    shape.render(x,y,size);
  }

}
class Document 
{
  Character[] data;
  ...
}

Note: Character delegates to CharacterShape.

Iterator

Problem: We want to do something to/with each element of a collection.

Traditional bad solution: Have something related to the collection that explicitly speaks to the position. (E.g., if the collection is an array, we have an i; if the collection is a list, we have a field called current)

  • May allow you to access data that you don’t have.
  • The client may not understand what the underlying representation is, and thereby start or stop at the wrong place.
  • Violates the principle of encapsulation; the client has to know way too much about the collection.
    • If the collection implementation changes, the client code may break (or may break other things)
  • Sometimes you want multiple simultaneous iterations. (Yes, there is a paper called “Lists with current considered harmful.”)

Solution: Allow classes to return Iterator objects. Normal model is Iterators provide advance, get, and maybe retreat and delete and replace.

Example: Deleting duplicates in a list.

Note: Iterators, like many of the core patterns, are increasingly built into languages

Adaptor

Problem: I want something that does X, I have something that does x. X and x are similar.

Solution: Build an object that responds to the needs of X by calling the similar things in x.

Example: I have a queue.

public class Queue<T>
{
  void enqueue(T val);
  T dequeue();
}

I want a ToDoList

public interface ToDoList<T>
{
  void addTask(T task);
  T nextTask();
}

We create

public class QueuedToDoList<T>
  implements ToDoList<T>
{
  Queue stuff;
  void addTask(T task)
  {
    stuff.enqueue(task);
  }
  T nextTask()
  {
    return stuff.dequeue()
  }
}

Or

public class QueuedToDoList<T>
  extends Queue
  implements ToDoList<T>
{
  void addTask(T task)
  {
    this.enqueue(task);
  }
  T nextTask()
  {
    return this.dequeue()
  }
}

Adaptors are one of many kinds of what most of us call “Wrappers”: They take an element of a class and “wrap” something around them (new functionality, additional functionality, etc.)

Example: We have a class, Course. Courses have multiple subclasses, EasyCourse, HardCourse. We decide we want to log each time a method is called.

public interface Course
{
  int getMyGrade();
}
public class EasyCourse
  implement Course
{
  int getMyGrade()
  {
    return 90 + random(15);
  }
}
public class HardCourse
  implement Course
{
  int getMyGrade()
  {
    if (random(20) == 1)
      return 90
    else
      return 70
  }
}
public class CountingCourse
  implements Course
{
  static int total_counter = 0;
  int my_counter = 0;
  Course trackme;
  public CountingCourse(Course _trackme)
  {
    this.trackme = trackme;
  }
  public int getMyGrade()
  {
    ++total_counter;
    ++my_counter;
    return trackme.getGrade();
  }
}
public class LoggingCourse
  implement Course
{
}

This only counts when we wrap the courses.

Old version

  Course csc151 = new EasyCourse(...);
  Course csc341 = new HardCourse(...);
  ....

New version

  Course csc151 = new CountingCourse(new EasyCourse(...));
  Course csc341 = new CountingCourse(new HardCourse(...));
  ....
  System.out.println("There were " + CountingCourse.total_counter + " calls.");
  System.out.println("CSC 151 accounted for " + ((CountingCourse) csc151).my_counter);

Logging version

  Course csc151 = new LoggingCourse("csc151", new EasyCourse(...));
  Course csc341 = new LoggingCourse("csc341", new HardCourse(...));
  ....

welcome to the world of decoration!

Observer

Factory