What can Pacman teach us?

A super simplified version of Pacman

  • Takes place in a grid
  • Game is turn-based: Pacman moves
    one square, then Ghosts moves one 
    square and so on.
  • Pacman and Ghosts move to an orthogal cell
  • There are 4 ghosts:
    Inky, Pinky, Blinky, Clyde
  • Inky moves directly to
    where Pacman is
  • Pinky moves to a cell 3 spaces
    ahead of where Pacman is
  • Blinky moves to strategic
    places on the board
  • Clyde behaves like Blinky if he
    is far from Pacman and like
    Inky if he is close
  • Player wins if Pacman eats all pills
  • If Pacman eats a Super Pill, then all
    Ghosts change their behavior and
    try to move away from it
  • If a Ghost moves to the same cell Pacman
    is, Pacman loses a life
  • If Pacman loses all his lives he loses

Game Board

The Game has a Board

public class Game {
    private char[][] board;
}

Is that the best we can do?

What about?

public class Book {
    private String authorName;
    private String publicationDate;
    private String ISBN;
}

What's a String?

  • A sequence of Unicode characters
  • It doesn't have a limit on its size

What about
Book's attributes?

  • What's a name? It can contain any character? Are there reasonable expectations about its length?
  • What's a publication date? It can contain any characters? Numbers are separated with: '/', '-', ' ', etc.? Are there reasonable expectations about its length?
  • What's an ISBN? It can contain any characters? Are there reasonable expectations about its length?

Beware this syndrome

public class Book {
    private Author author;
    private YearMonth publication; // Java 8
    private ISBN isbn;
}

A better design!

When you create a Type

  • You can define invariants
  • You don't need to validate it
    when you use it as a parameter
  • Your model is more expressive

What about... a triangle

  • A triangle has 3 angles: alpha, beta, gamma
  • and 3 sides: A, B, C
  • How you would define a class Triangle?
public class Triangle {
  private Side[] sides = new Side[3];
  private Angle[] angles = new Angle[3];
}

Is this what you were thinking?

The mark of a great
OOP developers is
that it loves Types!

Ok we need a Board
class, now what?

  • A board has rows and columns, in each square there is a tile: an empty tile, a wall, a pill, a super pill
  • So there are 4 kinds of tiles, but they are tiles anyway
public class Board {
  private Tile[][] tile;
}

Pretty simple, right?

public interface Tile {
   boolean isSolid();
}

But there is something to take into account

  • Tiles are stateless
  • But some of them
    are repeated a lot in a board!
  • It would be dreamy if there
    was a way to avoid a waste
    of memory!

Singleton

  • The most misused Design Pattern
  • Don't trust people that the first Design
    Pattern they can think of is Singleton :)

What is really a Singleton?

  • It's a class that should only have one instance

It's there something in the Java language that is related to this idea of one-instance-only?

Enums!

What's an enum?

  • An enum type is a special data type that enables for a variable to be a set of predefined constants.[1]
public enum Day {
   MONDAY, TUESDAY, WEDNESDAY, THURSDAY, 
   FRIDAY, SATURDAY, SUNDAY;
}

If each day is a constant,
it will be a big waste to have more
than one instance of each!

How Enums are implemented in Java?

  • Java creates an abstract class, e.g. Day
  • Then, it creates a constant for each value, which is a concrete class that extends the abstract class!
public enum Cell {

  DEAD {
    @Override
    public Cell nextGeneration(int aliveNeighbors) {
      if (aliveNeighbors == 3) {
        return ALIVE;
      }
      return this;
    }
  }, 

  ALIVE {
    @Override
    public Cell nextGeneration(int aliveNeighbors) {
      if (aliveNeighbors == 2 || aliveNeighbors == 3) {
        return this;
      }
      return DEAD;
    }
  };

  public abstract Cell nextGeneration(int aliveNeighbors);
}

The biggest problem implementing Singleton is truly ensuring that only one instance is created

If the JVM can do it for you, why you
would try to do it yourself!

But the biggest problem is understanding
when to use a Singleton

Each Tile is a
Singleton because...

  • There should only be one instance of each type,
    since each type is stateless

What other classes
should be Singletons?

  • Services which are usually stateless
    (but they have collaborators)
  • The default behaviour of Dependency Injection
    FW's is that they always return you the same
    instance of the bean you are asking for.

The key to use Singletons well
is: stateless
(or immutability)

public enum Tile {
  EMPTY {
    public String toString() {
      return " ";
    }
  },
  WALL {
    public boolean isSolid() {
      return true;
    }
    public String toString() {
      return "#";
    }
  },
  PILL {
    public String toString() {
      return ".";
    }
  },
  SUPER_PILL {
    public String toString() {
      return "o";
    }
  };

  public boolean isSolid() {
    return false;
  }
}

Now I got some implementations of a
given interface, how
do I instantiated them?

The obvious approach

Ship ship = new BattleShip();

Is there a
problem with new?

  • new creates coupling
  • classes from your model
    tend to be instantiated all
    over your code

But constructors are the only way to create objects, right?

Factory Method

  • If you are going to know just one
    Design Pattern, let it be this one!
  • A factory method, usually, 
    returns a concrete instance of
    an interface but the caller doesn't
    know (or care) which. 
  • You can use parameters to
    determine different implementations!
public class Ships {
  public static Ship build(String type) {
    switch(type) {
      case "BattleShip":
        return new BattleShip();
      case "AirCraft":
        return new Aircraft();
      case "Submarine":
        return new Submarine();
      case "Destroyer":
        return new Destroyer();
      case "Patrol":
        return new PatrolBoat();
      default:
        return null;
    }
  }   
}
public class Ships {
  public static Ship build(String type) {
    switch(type) {
      case "BattleShip":
        return new BattleShip();
      case "AirCraft":
        return new Aircraft();
      case "Submarine":
        return new Submarine();
      case "Destroyer":
        return new Destroyer();
      case "Patrol":
        return new PatrolBoat();
      default:
        return null;
    }
  }   
}

There are 2 major problems with this code!

The problem with Strings

  • Is it "Battleship"?
  • Is it "BATTLESHIP"?
  • Is it "BattleShip"?
  • Is it "battleship"?
  • ...

What's better?

Enums!

Enums

  • Remember, they are: A type that
    represents a finite set of constants
  • Compiler will check the spelling
  • Design is more expressive!
public enum Type {
  BATTLESHIP, AIRCRAFT, SUBMARINE, DESTROYER, PATROL;
}
public class Ships {
  public static Ship build(Type type) {
    switch(type) {
      case BATLLESHIP:
        return new BattleShip();
      case AIRCRAFT:
        return new Aircraft();
      case SUBMARINE:
        return new Submarine();
      case DESTROYER:
        return new Destroyer();
      case PATROL:
        return new PatrolBoat();
      default:
        return null;
    }
  }   
}

The other problem?

Returning null

  • There is nothing that stands out
    in the signature of a method when
    you may return null
  • If caller doesn't do a null-check,
    eventually you will get a NPE!
  • NPE is the billion-dollar mistake!

Why we were
returning null in
the first place?

public class Ships {
  public static Ship build(Type type) {
    switch(type) {
      case BATLLESHIP:
        return new BattleShip();
      case AIRCRAFT:
        return new Aircraft();
      case SUBMARINE:
        return new Submarine();
      case DESTROYER:
        return new Destroyer();
      case PATROL:
        return new PatrolBoat();
    }
  }   
}

Doesn't compile! Why?

There are possible paths
of execution that
don't return a value!

You may think that because it is an enum (a finite set of constant values), there are no more possible paths!

  • That's true... for now!
  • If a programmer adds a new kind of ship, e.g. TransformerShip, then that code would be broke
  • Remember that change is the only constant of
    software development!

Let's avoid returning null!

public class Ships {
  public static Ship build(Type type) {
    switch(type) {
      case BATLLESHIP:
        return new BattleShip();
      case AIRCRAFT:
        return new Aircraft();
      case SUBMARINE:
        return new Submarine();
      case DESTROYER:
        return new Destroyer();
      default:
        return new PatrolBoat();
    }
  }   
}

What's wrong now?

  • If we add a TransformerShip and forget to update our Factory Method, we will return a PatrolBoat and that is definitely not what we want!
  • We are silencing an error, we may never notice until our software goes to production!

A better approach

public class Ships {
  public static Ship build(Type type) {
    switch(type) {
      case BATLLESHIP:
        return new BattleShip();
      case AIRCRAFT:
        return new Aircraft();
      case SUBMARINE:
        return new Submarine();
      case DESTROYER:
        return new Destroyer();
      case PATROL:
        return new PatrolBoat();
      default:
        throw new AssertionError(String.format("Unrecognised type %s", type));
    }
  }   
}

Use AssertionError

  • To signal code that should have not been reached!
  • If someone adds a new boat type and forgets to update the Factory Method the method would throw an Error which will end the application, making the error visible!

Going back to the Factory Method Design Pattern

public class Board {
  Ship[] ships = { Ships.build(BATTLESHIP), 
      Ships.build(AIRCRAFT), Ships.build(SUBMARINE), 
      Ships.build(DESTROYER), Ships.build(PATROL)};
}

How code would use the Factory?

Wait! What if someone
has a different
approach to Ships?

public class Ships {
  public static Ship build(Type type) {
    return new BaseShip(type.getHitPoints());
  }   
}

Is this a better design?

  • We lack context, so who knows!
  • The important thing is that by using a Factory Method you decouple the creation of Ships with the code that uses, so you are free to experiment without breaking any code!

Where should a Factory Method be? And the concrete implementations?

  • Experiment to find the combination
    that suits your needs
  • Java 8 let's you have static methods
    on interfaces
  • You can use inner classes to hide the
    concrete implementation (Iterator, Path, etc.)
public enum Type {
  EMPTY, WALL, PILL, SUPER_PILL;

  public static Tile of(char representation) {
    switch (representation) {
      case ' ':
        return EMPTY;
      case '#':
        return WALL;
      case '.':
        return PILL;
      case 'o':
        return SUPER_PILL;
      default:
        throw new AssertionError(String.format(
            "Unsupported cell representation %s", representation));
    }
  }
}

Enough with
Tiles, let's go
back to Board

What's the relationship between Tile and Board?

public class Board {
  private final int height;
  private final int width;
  private final Map<Coordinate, Tile> board;
}

What's a Coordinate?

public class Coordinate {

  private final int row;
  private final int column;

  private Coordinate(int row, int column) {
    this.row = builder.row;
    this.column = builder.column;
  }

  public int getRow() {
    return row;
  }

  public int getColumn() {
    return column;
  }
}

Wait! Is this a coordinate
in row 1 column 2 or the other way around?

Coordinate coordinate = new Coordinate(2, 1);

Let's go back to Triangle

  • A Triangle has 6 attributes
  • But you can create a Triangle with less
    attributes and find the other ones
    (using Sine and Cosine Laws)
  • For the sake of simplicity let's suppose
    that you just need: An angle and 2 sides,
    or a side and 2 angles, or 3 sides.
  • But of course if you have those parameters and others, you should be able to use them!

Builder Pattern

  • If the creation of objects of a class
    is complex, create a class to deal with it.
  • By having a new class, you can offer
    flexibility to how to build objects of the class
    without breaking SRP
  • If you have a constructor with many
    parameters (or parameters of the same
    type) you can use an inner class with "setters"
  • If you are going to only know 2 patterns, let
    this be the second one!
public class Triangle {
   private final Side[] sides;

   public static class Builder {
     private final Side[] sides = new Side[3];
     public Builder withA(Side a) {
        side[0] = a;
        return this;
     }
     public Builder withB(Side b) {
        side[1] = b;
        return this;
     }
     public Builder withC(Side c) {
        side[2] = c;
        return this;
     }
     public Triangle build() {
        return new Triangle(this);
     }
   }

   public Triangle(Builder builder) {
     this.sides = builder.sides;
   }
}
Triangle t1 = new Triangle.Builder()
                          .withA(Side.of(3.0))
                          .withB(Side.of(4.0))
                          .withC(Side.of(5.0))
                          .build();

Triangle t2 = new Triangle.Builder()
                          .withAlpha(Angle.degrees(60.0))
                          .withC(Side.of(5.0))
                          .withBeta(Angle.degrees(60.0))
                          .build();

Triangle t3 = new Triangle.Builder()
                          .withA(Side.of(10.0))
                          .withGamma(Angle.degrees(90.0))
                          .withB(Side.of(10.0))
                          .build();

What about Coordinate?

public class Coordinate {
  private final int row;
  private final int column;

  public static class Builder {
    private int row;
    private int column;
    public Builder withRow(int row) {
      this.row = row;
      return this;
    }
    public Builder withColumn(int column) {
      this.column = column;
      return this;
    }
    public Coordinate build() {
      return new Coordinate(this);
    }
  }

  private Coordinate(Builder builder) {
    this.row = builder.row;
    this.column = builder.column;
  }
}

Now it's not possible to confuse rows and columns

Coordinate c1 = new Coordinate.Builder()
                              .withRow(1)
                              .withColumn(2)
                              .build();

but is it too much verbose for
just two attributes?

Software development
is never about
"can't" and "can"

It's about compromises

They are few design decision that don't have any redeemable quality

but they exists,
e.g. returning null!

More of Coordinate

it makes sense that a coordinate may be able to list all its neighbors, let's do that!

public class Coordinate {

  private static final int[] dx = {-1, 0, 0, 1};
  private static final int[] dy = { 0,-1, 1, 0};
  private static final int totalNeighbors = dx.length;

  private final int row;
  private final int column;

  // more code

  public List<Coordinate> neighbors() {
    List<Coordinate> neighbors = new ArrayList<>(totalNeighbors);
    for (int i = 0; i < totalNeighbors; ++i) {
      Coordinate neighbor = new Coordinate.Builder()
                                          .withRow(row + dx[i])
					  .withColumn(column + dy[i])
					  .build();
      neighbors.add(neighbor);
    }
    return Collections.unmodifiableList(neighbors);
  }
}

Yes I am!

Collections.unmodifiableList(neighbors);

Let's think about this:

  • You want to create a Collection that can't be modified
  • But you already have a lot of implementations
    of a particular interface . You want to still have
    those implementations but add the restriction to
    not let calls to method that modify its state.
  • Let me rephrase that: you want to add new behaviour to an existing class without modifying it!
  • That's so important that it has a name:
    the Open Closed Principle

How would you do it?

  • We want to take an existing
    implementation of List
  • Change the behaviour
    of some methods
  • Extra points if we can
    do it on runtime!
public class UnmodifiableList<E> implements List<E> {
  private List<E> data:

  public UnmodifiableList(List<E> component) {
     this.data = component;
  }

  @Override
  public int size() { // delegated
    return data.size();
  }

  @Override
  public boolean add(E e) { // decorated
     throw new UnsupportedOperationException();
  }

  // more code
}

Decorator Pattern

  • Create an interface to define the decorators
  • Each decorator takes a decorator
    as argument (usually in the constructor)
  • For each method you want to decorate: create a method that a delegates to the original method;
    write code after and / or before the call.

If you learn OOP with Java, chances are you already have used decorators

BufferedReader br = 
  new BufferedReader(new InputStreamReader(System.in));

BufferedReader br2 = 
  new BufferedReader(new FileReader("foo.txt"));

Going back to unmodifiableList

public static <T> List<T> unmodifiableList(List<? extends T> list)

It works by the magic of Polymorphism!

Try to explain that with the typical definitions of polymorphism!

Polymorphism

  • In OOP languages It's the feature that let you have a reference to an object of type T, to reference any object which is under the hierarchy tree of T
List<String> data = new ArrayList<>();

Ghosts

How to model your domain

  • Define new Types with an interface
    (never begin its name with I)
  • Create an abstract class to implement 
    methods common to its implementations,
    that way you avoid DRY!
  • Create concrete classes that extend
    the abstract class (Never end their
    name with impl)

Ghosts

  • Each ghost have a different behaviour
  • Some Ghosts change their behaviour
    depending on some factors
    (e.g. distance to Pacman)
  • All Ghosts change their behaviour once
    Pacman eats a Super Pill!
  • We need to change the implementation
    of a method on runtime!

Can we use the
Decorator Pattern?

  • Sometimes they are more than one way to do something, take into account
    the semantics!
  • Design Patterns become a common language, so when someone see a Decorator at first glance they will have certain expectations
  • Decorator Pattern use composition and delegation to decorate an existing method

Strategy Pattern

  • You want to change the behaviour
    of a class in runtime
  • The most common example used
    in the literature is if you want to
    change the sorting algorithm

That's not the best example!

A naive approach to change-behavior ghosts

public Coordinate move() {
  if (this.escaping) {
    // code to escape
  } else {
    // code to move normally
  }
}

What's the problem?

  • Code doesn't scale well, more behaviours
    mean more else path in the code which
    will make ir hard to understand!
  • It would possibly lead to repeating
    code across ghosts

Another possible approach 

public class ChaserGhost {
  public Coordinate move() {
    // code to chase
  }
}
public class Inky extends ChaserGhost {
}

What's the problem?

  • In Java classes can't extend
    more than one class
  • Inheritance is not
    correct in this case!
    (Favor composition
    over inheritance)

What's inheritance?

  • Inheritance provides a mechanism to create an specialisation of a class!
  • ChaserGhost is not an specialisation of Ghost!
    GenericServlet is an specialisation of Servlet.
    HttpServlet is an specialisation of GenericServlet.
    MyServlet is an specialisation of HttpServlet.
  • In our case, we are just trying to reuse code and for
    that we are using inheritance; That's not inheritance!

How do you reuse functionality without inheritance

  • Composition
  • Delegation
  • We have done something
    akin with Decorator Pattern

Strategy Pattern Step 1: Define an Interface

public class GhostBehavior {
  Coordinate move(Coordinate paceman, Board board);
}

Strategy Pattern Step 2: Define implementations

public class AmbushBehavior implements GhostBehavior {
  @Override
  public Coordinate move(Ghost ghost, Pacman pacman, Board board) {
    // code to ambush
  }
}
public class ChaseBehavior implements GhostBehavior {
  @Override
  public Coordinate move(Ghost ghost, Pacman pacman, Board board) {
    // code to chase
  }
}

Strategy Pattern Step 3: Changing mechanism

public abstract class CommonGhost {
  private GhostBehavior behavior;

  public CommonGhost(GhostBehavior behavior) {
    setBehavior(behavior);
  }

  public Coordinate move(Pacman paceman, Board board) {
    return behavior.move(paceman, board);
  }

  public void setBehavior(GhostBehavior behavior) {
    this.behavior = Objects.requireNotNull(behavior);
  }
}

Strategy Pattern Step 4
Now the Ghosts

public class Inky {

  public Inky() {
    super(new ChaseBehavior());
  }
}
public class Binky {

  public Binky() {
    super(new AmbushBehavior());
  }
}

The way to change behavior doesn't
need to be a setter

  • There could not be one!
  • There could a method
    that triggers the change!
  • Be creative!

How we can advise the ghosts to escape Pacman?

Ghosts need Game to
tell them when Pacman eats a Super Pill

  • They need to subscribe to notifications
  • Game will have a list of subscribers
  • When triggered, game will call a specific
    method on subscribers so they can do
    something meaningful
  • Code should decouple classes

Observer Pattern

public class Game {
  private Ghost[] ghosts = {new Inky(), new Blinky, 
      new Pinky(), new Clyde()};

  public void run() {
    // code
      if (tile == Tile.SUPER_PILL) {
        for (Ghost ghost: ghosts) {
          ghost.setBehavior(new EscapeBehavior());
        }
      }
    // code
  }
} 
public class ButtonDemo extends JPanel
                        implements ActionListener {
    protected JButton b1, b2, b3;
 
    public ButtonDemo() {
        b1 = new JButton("Disable middle button", leftButtonIcon);
        b1.setActionCommand("disable");
 
        b2 = new JButton("Middle button", middleButtonIcon);
 
        b3 = new JButton("Enable middle button", rightButtonIcon);
 
        //Listen for actions on buttons 1 and 3.
        b1.addActionListener(this);
        b3.addActionListener(this);
 
        //Add Components to this container, using the default FlowLayout.
        add(b1);
        add(b2);
        add(b3);
    }
 
    public void actionPerformed(ActionEvent e) {
        if ("disable".equals(e.getActionCommand())) {
            b2.setEnabled(false);
            b1.setEnabled(false);
            b3.setEnabled(true);
        } else {
            b2.setEnabled(true);
            b1.setEnabled(true);
            b3.setEnabled(false);
        }
    }
}

How do Ghost
go after Pacman?

  • A Graph is a collection of
    nodes and vertices
  • A vertice connects two nodes
  • If the vertices have a value associated,
    the graph is said to be weighted.
    Otherwise the graph is unweighted.

BFS (breadth first search)

  • BFS works on unweighted graphs
  • The possible states of a process
    can be seen as a graph
  • You begin with a source node
    and a destination source
int bfs(Node source, Node destination) {
  Queue<Coordinate> moves = new ArrayDeque<>();
  Map<Coordinate, Coordinate> antecessor = new HashMap<>();
  Map<Coordinate, Integer> distance = new HashMap<>();
  moves.add(origin);
  antecessor.put(origin, null);
  distance.put(origin, 0);

  while (!moves.isEmpty()) { 
    Node current = moves.poll();
    if (current.equals(destination)) {
      return distance.get(current);
    }
    for (Node neighbor: current.neighbors()) {
      if (!antecessor.containsKey(neighbor)) {
        moves.add(neighbor);
        antecesor.put(neighbor, current);
        distance.put(neighbor, distance.get(current) + 1);
      }			}
    }
  }

  return -1;
}

With BFS

  • You can chase Pacman
  • You can ambush Pacman
  • You can escape from Pacman

Design Patterns

Ok, seriously...

  • Again, it's constrains
  • Using Design Patterns you have more flexible code, what do you lose with them?
  • Maybe you are doing unrequested features?
  • Maybe Design Patterns is your hammer and everything looks like a nail?

Best way to learn
them is to code!

Iterate! Don't fear
erasing all your code
and staring again!

Resources

Q&A

Thanks!

@gaijinco
cobregon@hugeinc.com

What can Pacman teach us?

By Carlos Obregón

What can Pacman teach us?

A presentation that teaches some Design Patterns from a project-based approach: a super simplified version of Pacman

  • 2,407