Jade Dungeon

Clojure设计模式

Quick overview of the classic Design Patterns in Clojure

Disclaimer: Most patterns are easy to implement because we use dynamic typing, functional programming and, of course, Clojure. Some of them look wrong and ugly. It's okay. All characters are fake, coincidences are accidental.

Intro

Our programming language is fucked up. That's why we need design patterns. -- Anonymous

Two modest programmers Pedro Veel and Eve Dopler are solving common software engineering problems and applying design patterns.

Episode 1. Command

Leading IT service provider "Serpent Hill & R.E.E" acquired new project for USA customer. First delivery is a register, login and logout functionality for their brand new site.

Pedro: Oh, that's easy. You just need a Command interface...

interface Command {
  void execute();
}

Pedro: Every action should implement this interface and define specific execute behaviour.

public class LoginCommand implements Command {

  private String user;
  private String password;

  public LoginCommand(String user, String password) {
    this.user = user;
    this.password = password;
  }

  @Override
  public void execute() {
    DB.login(user, password);
  }
}
public class LogoutCommand implements Command {

  private String user;

  public LogoutCommand(String user) {
    this.user = user;
  }

  @Override
  public void execute() {
    DB.logout(user);
  }
}

Pedro: Usage is simple as well.

(new LoginCommand("django", "unCh@1ned")).execute();
(new LogoutCommand("django")).execute();

Pedro: What do you think, Eve?

Eve: Why are you using redundant wrapping into LoginCommand and just don't call DB.login?

Pedro: It's important to wrap here, because now we can operate on generic Command objects.

Eve: For what purpose?

Pedro: Delayed call, logging, history tracking, caching, plenty of usages.

Eve: Ok, how about that?

(defn execute [command]
  (command))

(execute #(db/login "django" "unCh@1ned"))
(execute #(db/logout "django"))

Pedro: What the hell this hash sign?

Eve: A shortcut for javaish

new SomeInterfaceWithOneMethod() {
  @Override
  public void execute() { // do }
};

Pedro: Just like Command interface...

Eve: Or if you want - no-hash solution.

(defn execute [command & args]
  (apply command args))

(execute db/login "django" "unCh@1ned")

Pedro: And how do you save function for delayed call in that case?

Eve: Answer yourself. What do you need to call a function?

Pedro: Its name...

Eve: And?

Pedro: ...arguments.

Eve: Bingo. All you do is saving a pair (function-name, arguments) and call it whenever you want using (apply function-name arguments)

Pedro: Hmm... Looks simple.

Eve: Definitely, Command is just a function.

Episode 2. Strategy

Sven Tori pays a lot of money to see a page with list of users. But users must be sorted by name and users with subscription must appear before all other users. Obviously, because they pay. Reverse sorting should keep subscripted users on top.

Pedro: Ha, just call Collections.sort(users, comparator) with custom comparator.

Eve: How would you implement custom comparator?

Pedro: You need to take Comparator interface and provide implementation for compare(Object o1, Object o2) method. Also you need another implementation for ReverseComparator

Eve: Stop talking, show me the code!

class SubsComparator implements Comparator<User> {

  @Override
  public int compare(User u1, User u2) {
    if (u1.isSubscription() == u2.isSubscription()) {
      return u1.getName().compareTo(u2.getName());
    } else if (u1.isSubscription()) {
      return -1;
    } else {
      return 1;
    }
  }
}

class ReverseSubsComparator implements Comparator<User> {

  @Override
  public int compare(User u1, User u2) {
    if (u1.isSubscription() == u2.isSubscription()) {
      return u2.getName().compareTo(u1.getName());
    } else if (u1.isSubscription()) {
      return -1;
    } else {
      return 1;
    }
  }
[[}]]

// forward sort
Collections.sort(users, new SubsComparator());

// reverse sort
Collections.sort(users, new ReverseSubsComparator());

Pedro: Could you do the same?

Eve: Yeah, something like that

(sort (comparator 
       (fn [u1 u2]
         (cond
          (= (:subscription u1)
             (:subscription u2)) (neg? (compare (:name u1)
                                                (:name u2)))
             (:subscription u1) true
             :else false))) users)

Pedro: Pretty similar.

Eve: But we can do it better

;; forward sort
(sort-by (juxt (complement :subscription) :name) users)

;; reverse sort
(sort-by (juxt :subscription :name) #(compare %2 %1) users)

Pedro: Oh my gut! Monstrous oneliners.

Eve: Functions, you know.

Pedro: Whatever, it's very hard to understand what's happening there.

Eve explains juxt, complement and sort-by

10 minutes later

Pedro: Very doubtful approach to pass strategy.

Eve: I don't care, because Strategy is just a function passed to another function.

Episode 3. State

Sales person Karmen Git investigated the market and decided to provide user-specific functionality.

Pedro: Smooth requirements.

Eve: Let's clarify them.

  • If user has subscription show him all news in a feed
  • Otherwise, show him only recent 10 news
  • If he pays money, add the amount to his account balance
  • If user doesn't have subscription and there is enough money to buy subscription, change his state to...

Pedro: State! Awesome pattern. First we make a user state enum

public enum UserState {
  SUBSCRIPTION(Integer.MAX_VALUE),
  NO_SUBSCRIPTION(10);

  private int newsLimit;

  UserState(int newsLimit) {
    this.newsLimit = newsLimit;
  }

  public int getNewsLimit() {
    return newsLimit;
  }
}

Pedro: User logic is following

public class User {
  private int money = 0;
  private UserState state = UserState.NO_SUBSCRIPTION;
  private final static int SUBSCRIPTION_COST = 30;

  public List<News> newsFeed() {
    return DB.getNews(state.getNewsLimit());
  }

  public void pay(int money) {
    this.money += money;
    if (state == UserState.NO_SUBSCRIPTION
        && this.money >= SUBSCRIPTION_COST) {
      // buy subscription
      state = UserState.SUBSCRIPTION;
      this.money -= SUBSCRIPTION_COST;
    }
  }
}

Pedro: Lets call it

User user = new User(); // create default user
user.newsFeed(); // show him top 10 news
user.pay(10); // balance changed, not enough for subs
user.newsFeed(); // still top 10
user.pay(25); // balance enough to apply subscription
user.newsFeed(); // show him all news

Eve: You just hide value that affects behaviour inside User object. We could use strategy to pass it directly user.newsFeed(subscriptionType).

Pedro: Agreed, State is very close to the Strategy. They even have the same UML diagrams. but we encapsulate balance and bind it to user.

Eve: I think it achieves the same goal using another mechanism. Instead of providing strategy explicitly, it depends on some state. From clojure perspective it can be implemented the same way as strategy pattern.

Pedro: But successive calls can change object's state.

Eve: Correct, but it has nothing to do with Strategy it is just implementation detail.

Pedro: What about "another mechanism"?

Eve: Multimethods.

Pedro: Multi what?

Eve: Look at this

(defmulti news-feed :user-state)

(defmethod news-feed :subscription [user]
  (db/news-feed))

(defmethod news-feed :no-subscription [user]
  (take 10 (db/news-feed)))

Eve: And pay function it's just a plain function, which changes state of object. We don't like state too much in clojure, but if you wish.

(def user (atom {:name "Jackie Brown"
                 :balance 0
                 :user-state :no-subscription}))

(def ^:const SUBSCRIPTION_COST 30)

(defn pay [user amount]
  (swap! user update-in [:balance] + amount)
  (when (and (>= (:balance @user) SUBSCRIPTION_COST)
             (= :no-subscription (:user-state @user)))
    (swap! user assoc :user-state :subscription)
    (swap! user update-in [:balance] - SUBSCRIPTION_COST)))

(news-feed @user) ;; top 10
(pay user 10)
(news-feed @user) ;; top 10
(pay user 25)
(news-feed @user) ;; all news

Pedro: Is dispatching by multimethods better than dispatching by enum?

Eve: No, in this particlular case, but in general yes.

Pedro: Explain, please

Eve: Do you know what double dispatch is?

Pedro: Not sure.

Eve: Well, it is topic for Visitor pattern.

Episode 4. Visitor

Natanius S. Selbys suggested to implement functionality which allows users export their messages, activities and achievements in different formats.

Eve: So, how do you plan to do it?

Pedro: We have one hierarchy for item types (Message, Activity) and another for file formats (PDF, XML)

abstract class Format { }
class PDF extends Format { }
class XML extends Format { }

public abstract class Item {
  void export(Format f) {
    throw new UnknownFormatException(f);
  }
  abstract void export(PDF pdf);
  abstract void export(XML xml);
}

class Message extends Item {
  @Override
  void export(PDF f) {
    PDFExporter.export(this);
  }

  @Override
  void export(XML xml) {
    XMLExporter.export(this);
  }
}

class Activity extends Item {
  @Override
  void export(PDF pdf) {
    PDFExporter.export(this);
  }

  @Override
  void export(XML xml) {
    XMLExporter.export(this);
  }
}

Pedro: That's all.

Eve: Nice, but how do you dispatch on argument type?

Pedro: What the problem?

Eve: Consider this snippet

Item i = new Activity();
Format f = new PDF();
i.export(f);

Pedro: Nothing suspicious here.

Eve: Actually, if you run this code you get UnknownFormatException

Pedro: Wait...Really?

Eve: In java you can use only single dispatch. That means if you call i.export(f) you dispatches on the actual type of i, not f.

Pedro: I'm surprised. So, there is no dispatch on argument type?

Eve: That's what visitor hack for. After you got a dispatch on i type, you additionally call f.someMethod(i) and dispatched on f type.

Pedro: How that looks in code?

Eve: You separately define export operations for all types as a Visitor

public interface Visitor {
  void visit(Activity a);
  void visit(Message m);
}

public class PDFVisitor implements Visitor {
  @Override
  public void visit(Activity a) {
    PDFExporter.export(a);
  }

  @Override
  public void visit(Message m) {
    PDFExporter.export(m);
  }
}

Eve: Your items change signature to accept different visitors.

public abstract class Item {
  abstract void accept(Visitor v);
}

class Message extends Item {
  @Override
  void accept(Visitor v) {
    v.visit(this);
  }
}

class Activity extends Item {
  @Override
  void accept(Visitor v) {
    v.visit(this);
  }
}

Eve: To use it you may call

Item i = new Message();
Visitor v = new PDFVisitor(); 
i.accept(v);

Eve: And everything works fine. Moreover, you can add new operations for activities and messages by just defining new visitors and without changing their code.

Pedro: That's really useful. But implementation is tough, it is the same for clojure?

Eve: Not really, clojure supports it natively via multimethods

Pedro: Multi what?

Eve: Just follow the code... First we define dispatcher function

(defmulti export
  (fn [item format] [(:type item) format]))

Eve: It accepts item and format to be exported. Examples:

;; Message
{:type :message :content "Say what again!"}
;; Activity
{:type :activity :content "Quoting Ezekiel 25:17"}
;; Formats
:pdf, :xml

Eve: And now you just provide a functions for different combinatations, and dispatcher decide which one to call.

(defmethod export [:activity :pdf] [item format]
  (exporter/activity->pdf item))

(defmethod export [:activity :xml] [item format]
  (exporter/activity->xml item))

(defmethod export [:message :pdf] [item format]
  (exporter/message->pdf item))

(defmethod export [:message :xml] [item format]
  (exporter/message->xml item))

Pedro: What if unknown format passed?

Eve: We could specify default dipatcher function.

(defmethod export :default [item format]
  (throw (IllegalArgumentException. "not supported")))

Pedro: Ok, but there is no hierarchy for :pdf and :xml. They are just keywords?

Eve: Correct, simple problem - simple solution. If you need advanced features, you could use adhoc hierarchies or dispatch by class.

(derive ::pdf ::format)
(derive ::xml ::format)

Pedro: Quadrocolons?!

Eve: Assume they are just keywords.

Pedro: Ok.

Eve: Then you add functions for every dispatch type ::pdf, ::xml and ::format

(defmethod export [:activity ::pdf])
(defmethod export [:activity ::xml])
(defmethod export [:activity ::format])

Eve: If some new format (i.e. csv) appears in the system

(derive ::csv ::format)

Eve: It will be dispatched to ::format function, until you add a separate ::csv function.

Pedro: Seems good.

Eve: Definitely, much easier.

Pedro: So, basically, if a language support multiple dispatch, you don't need Visitor pattern?

Eve: Exactly.

Episode 5. Template Method

MMORPG Mech Dominore Fight Saga requested to implement a game bot for their VIP users. Not fair.

Pedro: First, we must decide what actions should be automated with bot.

Eve: Have you ever played RPG?

Pedro: Fortunately, no

Eve: Oh my... Let's go, I'll show you...

2 weeks later

Pedro: ...fantastic, I found epic sword, which has +100 attack.

Eve: Unbelievable. But now, it's time for bot.

Pedro: Easy-peasy. We could select following events

  • Battle
  • Quest
  • Open Chest

Pedro: Characters behave differently in different events, for example mages cast spells in battle, but rogues prefer silent melee combat; locked chests are skipped by most characters, but rogues can unlock them, etc.

Eve: Looks like ideal candidate for Template Method?

Pedro: Yes. We define abstract algorithm, and then specify differences in subclasses.

public abstract class Character {
  void moveTo(Location loc) {
    if (loc.isQuestAvailable()) {
      Journal.addQuest(loc.getQuest());
    } else if (loc.containsChest()) {
      handleChest(loc.getChest());
    } else if (loc.hasEnemies()) {
      attack(loc.getEnemies());
    }
    moveTo(loc.getNextLocation());
  }

  private void handleChest(Chest chest) {
    if (!chest.isLocked()) {
      chest.open();
    } else {
      handleLockedChest(chest);
    }
  }

  abstract void handleLockedChest(Chest chest);
  abstract void attack(List<Enemy> enemies);
}

Pedro: We've separated to Character class everything common to all characters. Now we can create subclasses, that define how character should behave in specific situation. In out case: handling locked chests and attacking enemies.

Eve: Let's start with a Mage class.

Pedro: Mage? Okay. He can't open locked chest, so implementation is just do nothing. And if he is attacking enemies, if there are more than 10 enemies, freeze them, and cast teleport to run away. If there are 10 enemies or less cast fireball on each of them.

public class MageCharacter extends Character {
  @Override
  void handleLockedChest(Chest chest) {
    // do nothing
  }

  @Override
  void attack(List<Enemy> enemies) {
    if (enemies.size() > 10) {
      castSpell("Freeze Nova");
      castSpell("Teleport");
    } else {
      for (Enemy e : enemies) {
        castSpell("Fireball", e);
      }
    }
  }
}

Eve: Excellent, what about Rogue class?

Pedro: Easy as well, rogues can unlock chests and prefer silent combat, handle enemies one by one.

public class RogueCharacter extends Character {
  @Override
  void handleLockedChest(Chest chest) {
    chest.unlock();
  }

  @Override
  void attack(List<Enemy> enemies) {
    for (Enemy e : enemies) {
      invisibility();
      attack("backstab", e);
    }
  }
}

Eve: Excellent. But how this approach is differrent from Strategy?

Pedro: What?

Eve: I mean, you redefined behaviour by using subclasses, but in Strategy pattern you did the same: redefined behaviour by using functions.

Pedro: Well, another approach.

Eve: State was handled with another approach as well.

Pedro: What are you trying to say?

Eve: You are solving the same kind of problem, but change the approach to it.

Pedro: How do yo solve this problem using strategy in clojure?

Eve: Just pass a set of specific functions for each character. For example, your abstract move may look like:

(defn move-to [character location]
  (cond
   (quest? location)
   (journal/add-quest (:quest location))

   (chest? location)
   (handle-chest (:chest location))

   (enemies? location)
   (attack (:enemies location)))
  (move-to character (:next-location location)))

Eve: To add character-specific implementation of methods handle-chest and attack, implement them and pass as an argument.

;; Mage-specific actions
(defn mage-handle-chest [chest])

(defn mage-attack [enemies]
  (if (> (count enemies) 10)
    (do (cast-spell "Freeze Nova")
        (cast-spell "Teleport"))
    ;; otherwise
    (doseq [e enemies]
      (cast-spell "Fireball" e))))

;; Signature of move-to will change to

(defn move-to [character location
               & {:keys [handle-chest attack]
                  :or {handle-chest (fn [chest])
                       attack (fn [enemies] (run-away))}}]
  ;; previous implementation
)

Pedro: OMG, what's happening there?

Eve: We changed signature of move-to to accept handle-chest and attack functions. Think of them like optional parameters.

(move-to character location
  :handle-chest mage-handle-chest
  :attack       mage-attack)

Eve: Keep in mind that if these functions are not provided we use default behavior: do nothing for handle-chest and run away from enemies in attack

Pedro: Fine, but is this better than approach by subclassing? Seems that we have a lot of redundant information in move-to call.

Eve: It's fixable, just define this call once, and give it alias

(defn mage-move [character location]
  (move-to character location
    :handle-chest mage-handle-chest
    :attack       mage-attack))

Eve: Or use multimethods, it's even better.

(defmulti move 
  (fn [character location] (:class character)))

(defmethod move :mage [character location]
  (move-to character location
    :handle-chest mage-handle-chest
    :attack       mage-attack))

Pedro: I understand. But why do you think pass as argument is better than subclassing?

Eve: You can change behaviour dynamically. Assume your mage has no mana, so instead of trying to cast fireballs, he can just teleport and run away, you just provide new function.

Pedro: Makes sense. Functions everywhere.

Episode 6. Iterator

Technical consultant Kent Podiololis complains for C-style loops usage.
"Are we in 1980 or what?" -- Kent

Pedro: We definitely should use pattern Iterator from java.

Eve: Don't be fool, nobody's using java.util.Iterator

Pedro: Everybody use it implicitly in for-each loop. It's a good way to traverse a container.

Eve: What does it mean "to traverse a container"?

Pedro: Formally, the container should provide two methods for you: next() to return next element and hasNext() to return true if container has more elements.

Eve: Ok. Do you know what linked list is?

Pedro: Singly linked list?

Eve: Singly linked list.

Pedro: Sure. It is a container consists of nodes. Each node has a data value and reference to the next node. And null value if there is no next node.

Eve: Correct. Now tell me how traversing such list is differ from traversing via iterator?

Pedro: Emmm...

Pedro wrote two traversing snippets:

  • Traversing using iterator
Iterator i;
while (i.hasNext()) {
  i.next();
}
  • Traversing using linked list
Node next = root;
while (next != null) {
  next = next.next;
}

Pedro: They are pretty similar...What is analogue of Iterator in clojure?

Eve: seq function.

(seq [1 2 3])       => (1 2 3)
(seq (list 4 5 6))  => (4 5 6)
(seq #{7 8 9})      => (7 8 9)
(seq (int-array 3)) => (0 0 0)
(seq "abc")         => (\a \b \c)

Pedro: It returns a list...

Eve: Sequence, because Iterator is just a sequence

Pedro: Is it possible to make seq works on custom datastructures?

Eve: Implement clojure.lang.Seqable interface

(deftype RedGreenBlackTree [& elems]
  clojure.lang.Seqable
  (seq [self]
    ;; traverse element in needed order
    ))

Pedro: Fine then. But I've heard iterator is often used to achive laziness, for example to calculate value only during getNext() call, how list handle that?

Eve: List can be lazy as well, clojure calls such list "lazy sequence".

(def natural-numbers (iterate inc 1))

Eve: We defined thing to represent ALL natural numbers, but we haven't got OutOfMemory yet, because we haven't requested any value. It's lazy.

Pedro: Could you explain more?

Eve: Unfortunately, I am too lazy for that.

Pedro: I will remember that!

Episode 7: Memento

User Chad Bogue lost the message he was writing for two days. Implement save button for him.

Pedro: I don't believe there are people who can type in textbox for two days. Two. Days.

Eve: Let's save him.

Pedro: I googled this problem. Most popular approach to implement save button is Memento pattern. You need originator, caretaker and memento objects.

Eve: What's that?

Pedro: Originator is just an object or state that we want to preserve. (text inside a textbox), caretaker is responsible to save state (save button) and memento is just an object to encapsulate state.

public class TextBox {
  // state for memento
  private String text = "";

  // state not handled by memento
  private int width = 100;
  private Color textColor = Color.BLACK;

  public void type(String s) {
    text += s;
  }

  public Memento save() {
    return new Memento(text);
  }

  public void restore(Memento m) {
    this.text = m.getText();
  }

  @Override
  public String toString() {
    return "[" + text + "]";
  }
}

Pedro: Memento is just an immutable object

public final class Memento {
  private final String text;

  public Memento(String text) {
    this.text = text;
  }

  public String getText() {
    return text;
  }
}

Pedro: And caretaker is a demo

// open browser, init empty textbox
TextBox textbox = new TextBox();

// type something into it
textbox.type("Dear, Madonna\n");
textbox.type("Let me tell you what ");

// press button save
Memento checkpoint1 = textbox.save();

// type again
textbox.type("song 'Like A Virgin' is about. ");
textbox.type("It's all about a girl...");

// suddenly browser crashed, restart it, reinit textbox
textbox = new TextBox();

// but it's empty! All work is gone!
// not really, you rollback to last checkpoint
textbox.restore(checkpoint1);

Pedro: Just a note if you want a multiple checkpoints, save memento's to the list.

Eve: Originator, caretaker, memento - looks as a bunch of nouns, but actually it's all about two functions save and restore.

(def textbox (atom {}))

(defn init-textbox [] 
 (reset! textbox {:text ""
                  :color :BLACK
                  :width 100}))

(def memento (atom nil))

(defn type-text [text]
  (swap! textbox
    (fn [m]
      (update-in m [:text] (fn [s] (str s text))))))

(defn save []
  (reset! memento (:text @textbox)))

(defn restore []
  (swap! textbox assoc :text @memento))

Eve: And demo as well.

(init-textbox)
(type-text "'Like A Virgin' ")
(type-text "it's not about this sensitive girl ")
(save)
(type-text "who meets nice fella")
;; crash
(init-textbox)
(restore)

Pedro: It's pretty the same code.

Eve: Yes, but you must care about memento immutability

Pedro: What does it mean?

Eve: You are lucky, that you got String object in this example, String is immutable. But if you have something, that may change its internal state, you need to perform deep copy of this object for memento.

Pedro: Oh, right. It's just a recursive clone() calls to obtain prototype.

Eve: We will talk about Prototype in a minute, but just remember that Memento is not about caretaker and originator, it is about save and restore.

Episode 8: Prototype

Dex Ringeus detected that users feel uncomfortable with registration form. Make it more usable.

Pedro: So, what's the problem with the registration?

Eve: There are lot of fields users bored to type in.

Pedro: For example?

Eve: For example, weight. Having such field scares 90% of female users.

Pedro: But this field is important for our analytics system, we make food and clothes recomendations based on that field.

Eve: Then, make it optional, and if it is not provided, take some default value.

Pedro: 60 kg is ok?

Eve: I think so.

Pedro: Ok, give me two minutes.

2 hours later

Pedro: I suggest to use some registration prototype which has all fields are filled with default values. After user completes the form we modify filled values.

Eve: Sounds great.

Pedro: Here it is our standard registration form, with prototype in clone() method.

public class RegistrationForm implements Cloneable {
  private String name = "Zed";
  private String email = "zzzed@gmail.com";
  private Date dateOfBirth = new Date(1970, 1, 1);
  private int weight = 60;
  private Gender gender = Gender.MALE;
  private Status status = Status.SINGLE;
  private List<Child> children = Arrays.asList(new Child(Gender.FEMALE));
  private double monthSalary = 1000;
  private List<Brand> favouriteBrands = Arrays.asList("Adidas", "GAP");
  // few hundreds more properties

  @Override
  protected RegistrationForm clone() throws CloneNotSupportedException {
    RegistrationForm prototyped = new RegistrationForm();
      prototyped.name = name;
      prototyped.email = email;
      prototyped.dateOfBirth = (Date)dateOfBirth.clone();
      prototyped.weight = weight;
      prototyped.status = status;
      List<Child> childrenCopy = new ArrayList<Child>();
      for (Child c : children) {
        childrenCopy.add(c.clone());
      }
      prototyped.children = childrenCopy;
      prototyped.monthSalary = monthSalary;
      List<String> brandsCopy = new ArrayList<String>();
      for (String s : favouriteBrands) {
        brandsCopy.add(s);
      }
      prototyped.favouriteBrands = brandsCopy;
    return  prototyped;
  }
}

Pedro: Every time we create a user, call clone() and then override needed properties.

Eve: Awful! In mutable world clone() is needed to create new object with the same properties. The hard part is the copy must be deep, i.e. instead of copying reference you need recursively clone() other objects, and what if one of them doesn't have clone()...

Pedro: That's the problem and this pattern solves it.

Eve: I don't think it is a solution if you need to implement clone every time you adding new object.

Pedro: How clojure avoid this?

Eve: Clojure has immutable data structures. That's all.

Pedro: How does it solve prototype problem?

Eve: Every time you modify object, you get a fresh new immutable copy of your data, and old one is not changed. Prototype is not needed in immutable world

(def registration-prototype
     {:name          "Zed"
      :email         "zzzed@gmail.com"
      :date-of-birth "1970-01-01"
      :weight        60
      :gender        :male
      :status        :single
      :children      [{:gender :female}]
      :month-salary  1000
      :brands        ["Adidas" "GAP"]})

;; return new object
(assoc registration-prototype 
     :name "Mia Vallace"
     :email "tomato@gmail.com"
     :weight 52
     :gender :female
     :month-salary 0)

Pedro: Great! But how this affects performance? Copying million rows each time you adding new value seems time consuming operation.

Eve: No, it is not. Go to Google and search for persistent data structures and structural sharing

Pedro: Thanks a lot.

Episode 9: Mediator

Recently performed external code review shows a lot of issues with current codebase. Veerco Wierde emphasizes tight coupling in chat application.

Eve: What is tight coupling?

Pedro: It's the problem when objects know too much about each other.

Eve: Could you be more specific?

Pedro: Look at the current chat implementation

public class User {
  private String name;
  List<User> users = new ArrayList<User>();

  public User(String name) {
    this.name = name;
  }

  public void addUser(User u) {
    users.add(u);
  }

  void sendMessage(String message) {
    String text = String.format("%s: %s\n", name, message);
    for (User u : users) {
      u.receive(text);
    }
  }

  private void receive(String message) {
    // process message
  }
}

Pedro: The problem here is the user knows everything about other users. It is very hard to use and maintain such code. When new user connects to the chat, you must add a reference to him via addUser for every existing user.

Eve: So, we just move one piece of responsibility to another class?

Pedro: Yes, kind of. We create mega-aware class, called mediator, that binds all parts together. Obviously, each part knows only about mediator.

public class User {
  String name;
  private Mediator m;

  public User(String name, Mediator m) {
    this.name = name;
    this.m = m;
  }

  public void sendMessage(String text) {
    m.sendMessage(this, text);
  }

  public void receive(String text) {
    // process message
  }
}

public class Mediator {

  List<User> users = new ArrayList<User>();

  public void addUser(User u) {
    users.add(u);
  }

  public void sendMessage(User u, String text) {
    for (User user : users) {
      u.receive(text);
    }
  }
}

Eve: That seems like a simple refactoring problem.

Pedro: Profit may be underestimated, but if you have hundreds of mutually connected components (UI for example) mediator is really a savior.

Eve: Agreed.

Pedro: Now the clojure turn.

Eve: Ok...let's look...your mediator is responsible for saving users and sending messages

(def mediator
  (atom {:users []
         :send (fn [users text]
                 (map #(receive % text) users))}))

(defn add-user [u]
    (swap! mediator 
      (fn [m]
        (update-in m [:users] conj u))))

(defn send-message [u text]
    (let [send-fn (:send @mediator)
          users (:users @mediator)]
      (send-fn users (format "%s: %s\n" (:name u) text))))

(add-user {:name "Mister White"})
(add-user {:name "Mister Pink"})
(send-message {:name "Joe"} "Toby?")

Pedro: Good enough.

Eve: Nothing interesing here, because it is just a one approach to reduce coupling.

Episode 10: Observer

Independent security commision detected hacker Dartee Hebl has over a billion dollars balance on his account. Track big transactions to the accounts.

Pedro: Are we Holmses?

Eve: No, but there is a lack of logging in the system, need to find a way to track all changes to balance.

Pedro: We could add observers. Every time we modify balance, if change is big enough, notify about this and track the reason. We need Observer interface

public interface Observer {
  void notify(User u);
}

Pedro: And two specific observers

class MailObserver implements Observer {
  @Override
  public void notify(User user) {
    MailService.sendToFBI(user);
  }
}

class BlockObserver implements Observer {
  @Override
  public void notify(User u) {
    DB.blockUser(u);
  }
}

Pedro: Tracker class would be responsible for managing observers.

public class Tracker {
  private Set<Observer> observers = new HashSet<Observer>();

  public void add(Observer o) {
    observers.add(o);
  }

  public void update(User u) {
    for (Observer o : observers) {
      o.notify(u);
    }
  }
}

Pedro: And the last part: init tracker with the user and modify its addMoney method. If transcation amount is greaterthan 100$, notify FBI and block this user.

public class User {
  String name;
  double balance;
  Tracker tracker;

  public User() {
    initTracker();
  }

  private void initTracker() {
    tracker = new Tracker();
    tracker.add(new MailObserver());
    tracker.add(new BlockObserver());
  }


  public void addMoney(double amount) {
    balance += amount;
    if (amount > 100) {
      tracker.update(this);
    }
  }
}

Eve: Why are you created two separate observers? You could use it in a one.

class MailAndBlock implements Observer {
  @Override
  public void notify(User u) {
    MailService.sendToFBI(u);
    DB.blockUser(u);
  }
}

Pedro: Single responsibility principle.

Eve: Oh, yeah.

Pedro: And you can compose observer functionality dynamically.

Eve: I see your point.

;; Tracker

(def observers (atom #{}))

(defn add [observer]
  (swap! observers conj observer))

(defn notify [user]
  (map #(apply % user) @observers))

;; Fill Observers

(add (fn [u] (mail-service/send-to-fbi u)))
(add (fn [u] (db/block-user u)))

;; User

(defn add-money [user amount]
  (swap! user
    (fn [m]
      (update-in m [:balance] + amount)))
  ;; tracking
  (if (> amount 100) (notify)))

Pedro: It's a pretty the same way?

Eve: Yeah, in fact observer is just a way to register function, which will be called after another function.

Pedro: It is still the pattern.

Eve Sure, but we can improve solution a bit using clojure watches.

(add-watch
  user
  :money-tracker 
  (fn [k r os ns] 
    (if (< 100 (- (:balance ns) (:balance os)))
      (notify))))

Pedro: Why is that better?

Eve: First of all, our add-money function is clean, it just adds money. Also, watcher tracks every change to the state, not the ones we handle in mutator functions, like add-money

Pedro: Explain please.

Eve: If there is provided another secred method secret-add-money for changing balance, watchers will handle that as well.

Pedro: That's awesome!

Episode 11: Interpreter

Bertie Prayc stole important data from our server and shared it via BitTorrent system. Create a fake account for Bertie to discredit him.

Pedro: BitTorrent system based on .torrent files. We need to write Bencode encoder.

Eve: Yes, but first let's agree on the format spec

Bencode encoding rules:

  • Two datatypes are supported:
    • Integer N is encoded as i<N>e. (42 = i42e)
    • String S is encoded as <length>:<contents> (hello = 5:hello)
  • Two containers are supported:
    • List of values is encoded as l<contents>e ([1, "Bye"] = li1e3:Byee)
    • Map of values is encoded as d<contents>e ({"R" 2, "D" 2} = d1:Ri2e1:Di2ee)
      • Keys are just strings, Values are any bencode element

Pedro: Seems easy.

Eve: Maybe, but take into account that values may be nested, list inside list, etc.

Pedro: Sure. I think we can use Interpreter pattern for bencode encoding.

Eve: Try it.

Pedro: We start from interface for all bencode elements

interface BencodeElement {
  String interpret();
}

Pedro: Then we provide implementation for each datatype and datacontainer

class IntegerElement implements BencodeElement {
  private int value;

  public IntegerElement(int value) {
    this.value = value;
  }

  @Override
  public String interpret() {
    return "i" + value + "e";
  }
}

class StringElement implements BencodeElement {
  private String value;

  StringElement(String value) {
    this.value = value;
  }

  @Override
  public String interpret() {
    return value.length() + ":" + value;
  }
}

class ListElement implements BencodeElement {
  private List<? extends BencodeElement> list;

  ListElement(List<? extends BencodeElement> list) {
    this.list = list;
  }

  @Override
  public String interpret() {
    String content = "";
    for (BencodeElement e : list) {
      content += e.interpret();
    }
    return "l" + content + "e";
  }
}

class DictionaryElement implements BencodeElement {
  private Map<StringElement, BencodeElement> map;

  DictionaryElement(Map<StringElement, BencodeElement> map) {
    this.map = map;
  }

  @Override
  public String interpret() {
    String content = "";
    for (Map.Entry<StringElement, BencodeElement> kv : map.entrySet()) {
      content += kv.getKey().interpret() + kv.getValue().interpret();
    }
    return "d" + content + "e";
  }
}

Pedro: And finally, our bencoded string can be constructed from common datastructures programmatically

// discredit user
Map<StringElement, BencodeElement> mainStructure = new HashMap<StringElement, BencodeElement>();
// our victim
mainStructure.put(new StringElement("user"), new StringElement("Bertie"));
// just downloads files
mainStructure.put(new StringElement("number_of_downloaded_torrents"), new IntegerElement(623));
// and nothing uploads
mainStructure.put(new StringElement("number_of_uploaded_torrents"), new IntegerElement(0));
// and nothing donates
mainStructure.put(new StringElement("donation_in_dollars"), new IntegerElement(0));
// prefer dirty categories
mainStructure.put(new StringElement("preffered_categories"),
                      new ListElement(Arrays.asList(
                          new StringElement("porn"),
                          new StringElement("murder"),
                          new StringElement("scala"),
                          new StringElement("pokemons")
                      )));
BencodeElement top = new DictionaryElement(mainStructure);

// let's totally discredit him
String bencodedString = top.interpret();
BitTorrent.send(bencodedString);

Eve: Interesting, but that is ton of code!

Pedro: We pay readability for capabilities.

Eve: I suppose you've heard concept Code is Data, that's a lot easier in clojure

;; multimethod to handle bencode structure
(defmulti interpret class)

;; implementation of bencode handler for each type
(defmethod interpret java.lang.Long [n]
  (str "i" n "e"))

(defmethod interpret java.lang.String [s]
  (str (count s) ":" s))

(defmethod interpret clojure.lang.PersistentVector [v]
  (str "l" 
       (apply str (map interpret v))
       "e"))

(defmethod interpret clojure.lang.PersistentArrayMap [m]
  (str "d" 
       (apply str (map (fn [[k v]] 
                         (str (interpret k)
                              (interpret v))) m))
       "e"))

;; usage
(interpret {"user" "Bertie"
            "number_of_downloaded_torrents" 623
            "number_of_uploaded_torrent" 0
            "donation_in_dollars" 0
            "preffered_categories" ["porn"
                                    "murder"
                                    "scala"
                                    "pokemons"]})

Eve: You see how it is much easier define specific data?

Pedro: Sure, and interpret it's just a function per bencode type, instead of separate class.

Eve: Correct, interpreter is nothing but a set of functions to process a tree.

Episode 12: Flyweight

Administrators of the lawyer firm Cristopher, Matton & Pharts detected that reporting system consumes a lot of memory and garbage collector continiously hangs system for seconds. Fix that.

Pedro: I've seen this issue before.

Eve: What's wrong?

Pedro: They have realtime charts with a lot of different points. It's a huge amount of memory. As a result garbage collector stops the system.

Eve: Hmmm, what we can do?

Pedro: Not much, caching does not help us because points are...

Eve: Wait!

Pedro: What?

Eve: They use age values for points, why not precompute these points for most common ages? Say for age \([0, 100]\)

Pedro: You mean use Flyweight pattern?

Eve: I mean reuse objects.

class Point {
  int x;
  int y;

  /* some other properties*/

  // precompute 10000 point values at class loading time
  private static Point[][] CACHED;
  static {
    CACHED = new Point[100][];
    for (int i = 0; i < 100; i++) {
      CACHED[i] = new Point[100];
      for (int j = 0; j < 100; j++) {
        CACHED[i][j] = new Point(i, j);
      }
    }
  }

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  static Point makePoint(int x, int y) {
    if (x >= 0 && x < 100 &&
        y >= 0 && y < 100) {
      return CACHED[x][y];
    } else {
      return new Point(x, y);
    }
  }
}

Pedro: For this pattern we need two things: precompute most used points at a startup time, and use static factory method instead of constructor to return cached object.

Eve: Have you tested it?

Pedro: Sure, the system works like a clock.

Eve: Excellent, here is my version

(defn make-point [x y]
  [x y {:some "Important Properties"}])

(def CACHE
  (let [cache-keys (for [i (range 100) j (range 100)] [i j])]
      (zipmap cache-keys (map #(apply make-point %) cache-keys))))

(defn make-point-cached [x y]
  (let [result (get CACHE [x y])]
    (if result
      result
      (make-point x y))))

Eve: It creates a flat map with pair [x, y] as a key, instead of two-dimensional array.

Pedro: Pretty the same.

Eve: No, it is much flexible, you can't use two-dimensional array if you need to cache three points or non-integer values.

Pedro: Oh, got it.

Eve: Even better, in clojure you can just use memoize function to cache calls to factory function make-point

(def make-point-memoize (memoize make-point))

Eve: Every call (except first one) with the same parameters return cached value.

Pedro: That's awesome!

Eve: Of course, but remember if your function has side-effects, memoization is bad idea.

Episode 13: Builder

Tuck Brass complains that his old automatic coffee-making system is very slow in usage. Customers can't wait long enough and going away.

Pedro: Need to understand what's the problem exactly?

Eve: I've researched, system is old, written in COBOL and built around expert system question-answer. They were very popular long time ago.

Pedro: What do you mean by "question-answer"

Eve: There is operator in front of terminal. System asks: "Do yo want to add water?", operator answers "Yes". Then system asks again: "Do you want to add coffee", operator answers "Yes" and so forth.

Pedro: It's a nightmare, I just want coffee with milk. Why they don't use predefined options: coffee with milk, coffee with sugar and so on.

Eve: Because it is the raisin of the system: customer can make coffee with any set of ingridients by themselves

Pedro: Okay, let's fix it with Builder pattern.

public class Coffee {
  private String coffeeName; // required
  private double amountOfCoffee; // required
  private double water; // required
  private double milk; // optional
  private double sugar; // optional
  private double cinnamon; // optional

  private Coffee() { }

  public static class Builder {
    private String builderCoffeeName;
    private double builderAmountOfCoffee; // required
    private double builderWater; // required
    private double builderMilk; // optional
    private double builderSugar; // optional
    private double builderCinnamon; // optional

    public Builder() { }

    public Builder setCoffeeName(String name) {
      this.builderCoffeeName = name;
      return this;
    }

    public Builder setCoffee(double coffee) {
      this.builderAmountOfCoffee = coffee;
      return this;
    }

    public Builder setWater(double water) {
      this.builderWater = water;
      return this;
    }

    public Builder setMilk(double milk) {
      this.builderMilk = milk;
      return this;
    }

    public Builder setSugar(double sugar) {
      this.builderSugar = sugar;
      return this;
    }

    public Builder setCinnamon(double cinnamon) {
      this.builderCinnamon = cinnamon;
      return this;
    }

    public Coffee make() {
      Coffee c = new Coffee();
        c.coffeeName = builderCoffeeName;
        c.amountOfCoffee = builderAmountOfCoffee;
        c.water = builderWater;
        c.milk = builderMilk;
        c.sugar = builderSugar;
        c.cinnamon = builderCinnamon;

      // check required parameters and invariants
      if (c.coffeeName == null || c.coffeeName.equals("") ||
          c.amountOfCoffee <= 0 || c.water <= 0) {
        throw new IllegalArgumentException("Provide required parameters");
      }

      return c;
    }
  }
}

Pedro: As you see, you can't instantiate Coffee class easily, you need to set parameters with nested Builder class

Coffee c = new Coffee.Builder()
        .setCoffeeName("Royale Coffee")
        .setCoffee(15)
        .setWater(100)
        .setMilk(10)
        .setCinnamon(3)
        .make();

Pedro: Calling to method make checks all required parameters, and could validate and throw an exception if object is in inconsistent state.

Eve: Awesome functionality, but why so verbose?

Pedro: Beat it.

Eve: A piece of cake, clojure supports optional arguments, everything what builder pattern is about.

(defn make-coffee [name amount water
                   & {:keys [milk sugar cinnamon]
                      :or {milk 0 sugar 0 cinnamon 0}}]
  ;; definition goes here
  )

(make-coffee "Royale Coffee" 15 100
             :milk 10
             :cinnamon 3)

Pedro: Aha, you have three required parameters and three optionals, but required parameters still without names.

Eve: What do you mean?

Pedro: From the client call I see number 15 but I have no idea what it might be.

Eve: Agreed. Then, let's make all parameters are named and add precondition for required, the same way you do with the builder.

(defn make-coffee
  [& {:keys [name amount water milk sugar cinnamon]
      :or {name "" amount 0 water 0 milk 0 sugar 0 cinnamon 0}}]
  {:pre [(not (empty? name))
         (> amount 0)
         (> water 0)]}
  ;; definition goes here        
  )

(make-coffee :name "Royale Coffee"
             :amount 15
             :water 100
             :milk 10
             :cinnamon 3)

Eve: As you see all parameters are named and all required params are checked in :pre constraint. If constraints are violated AssertionError is thrown.

Pedro: Interesting, :pre is a part of a language?

Eve: Sure, it's just a simple assertion. There is also :post constraint, with the similar effect.

Pedro: Hm, okay. But as you know Builder pattern often used as a mutable datastucture, StringBuilder for example.

Eve: It's not a part of clojure philosophy to use mutables, but if you really want, no problem. Just create a new class with deftype and do not forget to use volatile-mutable on the properties you want to mutate.

Pedro: Where is the code?

Eve: Here is example of custom implementation of mutable StringBuilder in clojure. It has a lot of drawbacks and limitations but you've got the idea.

;; interface
(defprotocol IStringBuilder
    (append [this s])
    (to-string [this]))

;; implementation
(deftype ClojureStringBuilder [charray ^:volatile-mutable last-pos]
    IStringBuilder
    (append [this s] 
      (let [cs (char-array s)]
        (doseq [i (range (count cs))]
          (aset charray (+ last-pos i) (aget cs i))))
      (set! last-pos (+ last-pos (count s))))
    (to-string [this] (apply str (take last-pos charray))))

;; clojure binding
(defn new-string-builder []
  (ClojureStringBuilder. (char-array 100) 0))

;; usage
(def sb (new-string-builder))
(append sb "Toby Wong")
(to-string sb) => "Toby Wong"
(append sb " ")
(append sb "Toby Chung") => "Toby Wang Toby Chung"

Pedro: Not as hard as I thought.

Episode 14: Facade

Our new member Eugenio Reinn Jr. commited file with diff in 134 lines to processing servlet. But actual work is just to process request. All other code injection and imports. It MUST be one line commit.

Pedro: Who cares how many lines are committed?

Eve: Someone cares.

Pedro: Let's see where is the problem

class OldServlet {
  @Autowired
  RequestExtractorService requestExtractorService;
  @Autowired
  RequestValidatorService requestValidatorService;
  @Autowired
  TransformerService transformerService;
  @Autowired
  ResponseBuilderService responseBuilderService;

  public Response service(Request request) {
    RequestRaw rawRequest = requestExtractorService.extract(request);
    RequestRaw validated = requestValidatorService.validate(rawRequest);
    RequestRaw transformed = transformerService.transform(validated);
    Response response = responseBuilderService.buildResponse(transformed);
    return response;
  }
}

Eve: Oh shi...

Pedro: That's our internal API for developers, every time they need to process request, inject 4 services, include all imports, and write this code.

Eve: Let's refactor it with...

Pedro: ...Facade pattern. We resolve all dependencies to a single point of access and simplify API usage.

public class FacadeService {
  @Autowired
  RequestExtractorService requestExtractorService;
  @Autowired
  RequestValidatorService requestValidatorService;
  @Autowired
  TransformerService transformerService;
  @Autowired
  ResponseBuilderService responseBuilderService;

  RequestRaw extractRequest(Request req) {
    return requestExtractorService.extract(req);
  } 

  RequestRaw validateRequest(RequestRaw raw) {
    return requestValidatorService.validate(raw);
  }

  RequestRaw transformRequest(RequestRaw raw) {
    return transformerService.transform(raw);
  }

  Response buildResponse(RequestRaw raw) {
    return responseBuilderService.buildResponse(raw);
  }
}

Pedro: Then if you need any service or set of services in the code you just injecting facade to your code

class NewServlet {
  @Autowired
  FacadeService facadeService;

  Response service(Request request) {
    RequestRaw rawRequest = facadeService.extractRequest(request);
    RequestRaw validated = facadeService.validateRequest(rawRequest);
    RequestRaw transformed = facadeService.transformRequest(validated);
    Response response = facadeService.buildResponse(transformed);
    return response;
  }
}

Eve: Wait, you've just moved all dependencies to one and everytime using this one, correct?

Pedro: Yes, now everytime some functionality is needed, use FacadeService. Dependency is already there.

Eve: But we did the same in 「Mediator」 pattern?

Pedro: Mediator is behavioral pattern. We resolved all dependency to Mediator and added new behavior to it.

Eve: And facade?

Pedro: Facade is structural, we don't add new functionality, we just expose existing functionality with facade.

Eve: Got it. But seems that pattern very loud word for such little tweak.

Pedro: Maybe.

Eve: Here is clojure version using structure by namespaces

(ns application.old-servlet
  (:require [application.request-extractor :as re])
  (:require [application.request-validator :as rv])
  (:require [application.transformer :as t])
  (:require [application.response-builder :as rb]))

(defn service [request]
  (-> request
      (re/extract)
      (rv/validate)
      (t/transform)
      (rb/build)))

Eve: Exposing all services via facade.

(ns application.facade
  (:require [application.request-extractor :as re])
  (:require [application.request-validator :as rv])
  (:require [application.transformer :as t])
  (:require [application.response-builder :as rb]))

(defn request-extract [request]
  (re/extract request))

(defn request-validate [request]
  (rv/validate request))

(defn request-transform [request]
  (t/transform request))

(defn response-build [request]
  (rb/build request))

Eve: And use it.

(ns application.old-servlet
  (:use [application.facade]))

(defn service [request]
  (-> request
      (request-extract)
      (request-validate)
      (request-transform)
      (request-build)))

Pedro: What the difference between :use and :require?

Eve: They are almost similar, but with :require you expose functionality via namespace qualificator (namespace/function) where with :use you can refer to it directly (function)

Pedro: So, :use is better.

Eve: No, be aware of :use because it can conflict with existing names in your namespace.

Pedro: Oh, I see your point. And every time you call (:use [application.facade]) in some namespace all existing functionality from facade is available?

Eve: Yes.

Pedro: Pretty the same.

Episode 15: Singleton

Feverro O'Neal complains that we have a lot of different styles for UI. Force one per application UI configuration.

Pedro: But wait, there was requirement to save UI style per user.

Eve: Probably it was changed.

Pedro: Ok, then we should just save configuration to Singleton and use it from all the places.

public final class UIConfiguration {
  public static final UIConfiguration INSTANCE = new UIConfiguration("ui.config");

  private String backgroundStyle;
  private String fontStyle;
  /* other UI properties */

  private UIConfiguration(String configFile) {
    loadConfig(configFile);
  }

  private static void loadConfig(String file) {
    // process file and fill UI properties
    INSTANCE.backgroundStyle = "black";
    INSTANCE.fontStyle = "Arial";
  }

  public String getBackgroundStyle() {
    return backgroundStyle;
  }

  public String getFontStyle() {
    return fontStyle;
  }
}

Pedro: That way all configuration will be shared across the UIs.

Eve: Yes, but...why so much code?

Pedro: We guarantee that only one instance of UIConfiguration will exist.

Eve: Let me ask you: what's the difference between singleton and global varaible.

Pedro: What?

Eve: ...the difference between singleton and global variable.

Pedro: Java does not support global variables.

Eve: But UIConfiguration.INSTANCE is global variable.

Pedro: Well, sort of.

Eve: Your code is just simple def in clojure.

(def ui-config (load-config "ui.config"))

(defn load-config [config-file]
  ;; process config file and return map with configuratios
  {:bg-style "black" :font-style "Arial"})

Pedro: But, how do you change the style?

Eve: The same way you will change it in your code.

Pedro: Uhm... Ok, we need a simple tweak. Make UIConfiguration.loadConfig is public and call it when the configuration changes.

Eve: Then we make ui-config an atom and call swap! when configuration changes.

Pedro: But atoms are useful only in concurrent environment.

Eve: First, yes, they useful, but NOT only in concurrent environment. Second, atom read is not as slow as you think. Third, it changes the state of UI configuration atomically

Pedro: It is redundant for such simple example.

Eve: No, it is not. There is a posibility that UI configuration changes and some renders read new backgroundStyle, but old fontStyle

Pedro: Ok, use synchronized for loadConfig

Eve: Then you must use synchonized on getters as well, it is slow.

Pedro: There is still Double-Checked Locking idiom

Eve: Double-checked locking is clever but broken

Pedro: Ok, I give up, you won.

Episode 16: Chain Of Responsibility

New York marketing organization "A Profit NY" opened request to filter profanity words from their public chat system.

Pedro: Fuck, they don't like the word "fuck"?

Eve: It is profit organization, they lose money if someone use profanity words in public chat.

Pedro: Who defined profanity words list?

Eve: George Carlin

Watching and laughing

Pedro: Ok, so let's just add a filter to replace these rude words with the asterisks.

Eve: Make sure your solution is extendable, other filters could be applied.

Pedro: Chain of Responisibility seems like a good pattern candidate for that. First of all we make some abstract filter.

public abstract class Filter {
  protected Filter nextFilter;

  abstract void process(String message);

  public void setNextFilter(Filter nextFilter) {
    this.nextFilter = nextFilter;
  }
}

Pedro: Then, provide implementation for each specific filter you want to apply

class LogFilter extends Filter {
  @Override
  void process(String message) {
    Logger.info(message);
    if (nextFilter != null) nextFilter.process(message);
  }
}

class ProfanityFilter extends Filter {
  @Override
  void process(String message) {
    String newMessage = message.replaceAll("fuck", "f*ck");
    if (nextFilter != null) nextFilter.process(newMessage);
  }
}

class RejectFilter extends Filter {
  @Override
  void process(String message) {
    System.out.println("RejectFilter");
    if (message.startsWith("[A PROFIT NY]")) {
      if (nextFilter != null) nextFilter.process(message);
    } else {
      // reject message - do not propagate processing
    }
  }
}

class StatisticsFilter extends Filter {
  @Override
  void process(String message) {
    Statistics.addUsedChars(message.length());
    if (nextFilter != null) nextFilter.process(message);
  }
}

Pedro: And finally build a chain of filters which defines an order how message will be processed.

Filter rejectFilter = new RejectFilter();
Filter logFilter = new LogFilter();
Filter profanityFilter = new ProfanityFilter();
Filter statsFilter = new StatisticsFilter();

rejectFilter.setNextFilter(logFilter);
logFilter.setNextFilter(profanityFilter);
profanityFilter.setNextFilter(statsFilter);

String message = "[A PROFIT NY] What the fuck?";
rejectFilter.process(message);

Eve: Ok, now clojure turn. Just define each filter as a function.

;; define filters

(defn log-filter [message]
  (logger/log message)
  message)

(defn stats-filter [message]
  (stats/add-used-chars (count message))
  message)

(defn profanity-filter [message]
  (clojure.string/replace message "fuck" "f*ck"))

(defn reject-filter [message]
  (if (.startsWith message "[A Profit NY]")
    message))

Eve: And use `some->` macro to chain filters

{{{class="brush: clojure"
(defn chain [message]
  (some-> message
          reject-filter
          log-filter
          stats-filter
          profanity-filter))

Eve: You see how much it is easier, you don't need every-time call if (nextFilter != null) nextFilter.process(), because it's natural. The next filter defined at the some-> level naturally, instead of calling manuall setNext.

Pedro: That's definitely better for composability, but why did you use some-> instead of ->?

Eve: Just for reject-filter. It could stop further processing, so some-> returns nil as soon as nil encountered as a filter

Pedro: Could you explain more?

Eve: Look at the usage

(chain "fuck") => nil
(chain "[A Profit NY] fuck") => "f*ck"

Pedro: Understood.

Eve: Chain of Responsibility just an approach to function composition

Episode 17: Composite

Actress Bella Hock don't see user avatars in our social network on her computer.
"Everything is black. Is it black holes?"

Pedro: It is black squares.

Eve: Hmm, the same problem on our side.

Pedro: Seems, that latest feature broke user avatars.

Eve: Strange, because avatars rendered the same way as other elements, but they are visible.

Pedro: Are you sure it rendered the same way?

Eve: Well...no

Digging code

Pedro: What the hell is going on here?

Eve: Someone copypasted code, but forgot to reflect changes in avatars.

Pedro: Let's blame him, git-blame

Eve: Blame is good, but we need to fix the problem

Pedro: It's simple just additional line here.

Eve: I mean, really solve the problem. Why do we need two similar snippets of code to process the same blocks?

Pedro: Correct, I think we could use Composite pattern to handle rendering of the whole page. Our smallest element to render is block.

public interface Block {
  void addBlock(Block b);
  List<Block> getChildren();

  void render();
}

Pedro: Obviously blocks may contain other blocks, that's the point of Composite pattern. We may create some specific blocks.

public class Page implements Block { }
public class Header implements Block { }
public class Body implements Block { }
public class HeaderTitle implements Block { }
public class UserAvatar implements Block { }

Pedro: And treat every specific element as a Block

Block page = new Page();
Block header = new Header();
Block body = new Body();
Block title = new HeaderTitle();
Block avatar = new UserAvatar();

page.addBlock(header);
page.addBlock(body);
header.addBlock(title);
header.addBlock(avatar);

page.render();

Pedro: This is a structural pattern, a good way to compose objects. That's why it is called composite

Eve: Hey, composite is a simple tree structure.

Pedro: Yes.

Eve: There is pattern for every datastructure?

Pedro: No, just for list and tree.

Eve: Actually, tree can be represented as a list

Pedro: How?

Eve: First element of a list is a node value, next elements are children, each of them is...

Pedro: I understand.

Eve: To be specific, here is the tree

        A
     /  |  \
    B   C   D
    |   |  / \
    E   H J   K
   / \       /|\
  F   G     L M N

Eve: And here is the list represents this tree

(def tree
  '(A (B (E (F) (G))) (C (H)) (D (J) (K (L) (M) (N)))))

Pedro: It's a plenty of parentheses!

Eve: They define structure, you know

Pedro: But it's hard to understand

Eve: It's easy for machine, there is awesome tree-seq function to process the tree.

(map first (tree-seq next rest tree)) => (A B E F G C H D J K L M N)

Eve: If you need more advanced traversals, use clojure.walk

Pedro: I don't know, everything seems just a bit harder.

Eve: No, you define the whole tree with one datastructure and use one function to operate on it.

Pedro: What this function will do?

Eve: It traverses the tree and applies to every node, so in our case it can render each component.

Pedro: I don't know, maybe I am too young for the trees, let's move forward.

Episode 18. Factory Method

Sir Dry Bang suggest to create new levels for their popular game. More levels - more money.

Pedro: How can we create new levels?

Eve: Just change assets and add new blocks: paper, wood, iron...

Pedro: It's too silly, isn't it?

Eve: The whole game is too silly. If user pay for color hats for their characters, then they will pay for wooden blocks as well.

Pedro: I think it's a crap, but anyway, let's make MazeBuilder generic and add specific builder for each type of the block. It's a Factory Method pattern.

class Maze { }
class WoodMaze extends Maze { }
class IronMaze extends Maze { }

interface MazeBuilder {
  Maze build();
}

class WoodMazeBuilder {
  @Override
  Maze build() {
    return new WoodMaze();
  }
}

class IronMazeBuilder {
  @Override
  Maze build() {
    return new IronMaze();
  }
}

Eve: Isn't it obvious that IronMazeBuilder will return IronMazes?

Pedro: Not for program. But see, to make a maze from another blocks we just change implementation responsible for block creation.

MazeBuilder builder = new WoodMazeBuilder();
Maze maze = builder.build();

Eve: I've seen something similar before.

Pedro: What exactly?

Eve: For me it seems like a strategy or state pattern.

Pedro: No way! Strategy is about performing specific operations and factory is for creating specific object.

Eve: But create is an operation as well.

(defn maze-builder [maze-fn])

(defn make-wood-maze [])
(defn make-iron-maze [])

(def wood-maze-builder (partial maze-builder make-wood-maze))
(def iron-maze-builder (partial maze-builder make-iron-maze))

Pedro: Hm, seems similar.

Eve: Think about it.

Pedro: Any usage examples?

Eve: No, everything is obvious here, just re-read 「Strategy」, 「State」 or 「TemplateMethod」 episodes.

Episode 19: Abstract Factory

Users are not buying new levels in the game. Saimank Gerr build a complains cloud and the most popular negative feedback words are: "ugly", "crap" and "shit".
Improve levels-building system.

Pedro: I said this is crap.

Eve: Sure, snow background with wooden walls, space invaders with wooden walls every setting with wooden walls.

Pedro: Then we must separate game worlds and build a set of specific objects for particular world.

Eve: Explain.

Pedro: Instead of using Factory Method for building specific blocks, we use Abstract Factory to build a set of related objects, to make a level look less crappy.

Eve: Example would be good.

Pedro: Code is my example. First we define abstract behaviour of level factory

public interface LevelFactory {
  Wall buildWall();
  Back buildBack();
  Enemy buildEnemy();
}

Pedro: Then we have a hierarchy of objects that level consists of

class Wall {}
class PlasmaWall extends Wall {}
class StoneWall extends Wall {}

class Back {}
class StarsBack extends Back {}
class EarthBack extends Back {}

class Enemy {}
class UFOSoldier extends Enemy {}
class WormScout extends Enemy {}

Pedro: See? We have a specific object for each level, let's create factory for them.

class SpaceLevelFactory implements LevelFactory {
  @Override
  public Wall buildWall() {
    return new PlasmaWall();
  }

  @Override
  public Back buildBack() {
    return new StarsBack();
  }

  @Override
  public Enemy buildEnemy() {
    return new UFOSoldier();
  }
}

class UndergroundLevelFactory implements LevelFactory {
  @Override
  public Wall buildWall() {
    return new StoneWall();
  }

  @Override
  public Back buildBack() {
    return new EarthBack();
  }

  @Override
  public Enemy buildEnemy() {
    return new WormScout();
  }
}

Pedro: Each implementation of level factory creates related objects for level. Levels will look less crappy for sure.

Eve: Let me understand. I really can't spot the difference.

Pedro: Factory Method defers object creation to a subclasses, Abstract Factory do the same but for a set of related object.

Eve: Aha, that means I need to pass set of related functions to abstract builder

(defn level-factory [wall-fn back-fn enemy-fn])

(defn make-stone-wall [])
(defn make-plasma-wall [])

(defn make-earth-back [])
(defn make-stars-back [])

(defn make-worm-scout [])
(defn make-ufo-soldier [])

(def underground-level-factory
  (partial level-factory
           make-stone-wall
           make-earth-back
           make-worm-scout))

(def space-level-factory
  (partial level-factory
           make-plasma-wall
           make-stars-back
           make-ufo-soldier))

Pedro: I knew.

Eve: Everything is fair. Your lovely "set of related Xs", where X is a function

Pedro: Yes, clarify, what partial is.

Eve: Provide some parameters for function. So, underground-level-factory knows how to construct walls, backs and enemies. Everything other inherited from abstract level-factory function.

Pedro: Handy.

Episode 20: Adapter

Deam Evil conducts a medieval tournament for knights. The prize is $100.000
I'll pay you the half if you break the system and allow my armed commando to take part in competition.

Pedro: Finally we've got interesting work.

Eve: Funny to see the competition. Especially, M16 vs Iron Sword part.

Pedro: Knights have a good armor.

Eve: F1 grenade does not care about armor.

Pedro: Nevermind, we do the work, we get the money.

Eve: Fifty grands - nice compensation

Pedro: Yes, look at this, I've stolen sources of the competition system, though it is not possible to modify their sources, we can find some vulneravility.

Eve: Here it is

public interface Tournament {
  void accept(Knight knight);
}

Pedro: Aha! System validates only incoming types via Knight interface. All we need to do is to adapt commando to be a knight. Let's see how knight look like

interface Knight {
  void attackWithSword();
  void attackWithBow();
  void blockWithShield();
}

class Galahad implements Knight {
  @Override
  public void blockWithShield() {
    winkToQueen();
    take(shield);
    block();
  }

  @Override
  public void attackWithBow() {
    winkToQueen();
    take(bow);
    attack();
  }

  @Override
  public void attackWithSword() {
    winkToQueen();
    take(sword);
    attack();
  }
}

Pedro: To accept the commando let's take an old implementation

class Commando {
    void throwGrenade(String grenade) { }
    shot(String rifleType) { }
}

Pedro: And adapt it.

class Commando implements Knight {
  @Override
  public void blockWithShield() {
    // commando don't block
  }

  @Override
  public void attackWithBow() {
    throwGrenade("F1");
  }

  @Override
  public void attackWithSword() {
    shotWithRifle("M16");
  }
}

Pedro: That's it.

Eve: It's simpler in clojure.

Pedro: Really?

Eve: We don't love types so their validation won't work at all

Pedro: So, how do you replace knight with commando?

Eve: Basically, what knight is? It's a map, consists of data and behaviour

{:name "Lancelot"
 :speed 1.0
 :attack-bow-fn attack-with-bow
 :attack-sword-fn attack-with-sword
 :block-fn block-with-shield}

Eve: To adapt commando, just pass his functions instead original ones

{:name "Commando"
 :speed 5.0
 :attack-bow-fn (partial throw-grenade "F1")
 :attack-sword-fn (partial shot "M16")
 :block-fn nil}

Pedro: How did we share money?

Eve: 50/50

Pedro: I wrote more code, I want 70

Eve: Ok, 70/70

Pedro: Deal.

Episode 21: Decorator

Podrea Vesper caught us on cheating for the tournament. We have a choice: to be busted by police or to help his super knight to take part in competition

Pedro: I don't wanna go to prison.

Eve: Me either.

Pedro: Let's cheat for him one more time.

Eve: This is the same solution, isn't it?

Pedro Similar, but not the same. Commando was a soldier and they are not allowed on the tournament. We adapted it. But knight is allowed for tournament we don't need to adapt it, we must add functionality to existing object.

Eve: Inheritance or composition?

Pedro: Composition, the main goal of decorator to change behaviour at a runtime

Eve: So how we deal with this super knight?

Pedro: They plan to use Galahad knight and decorate it with more HP and Power Armor

Eve: Heh, funny that cops are playing Fallout

Pedro: Yeah, let's make knight an abstract class

public class Knight {
    protected int hp;
    private Knight decorated;

    public Knight() { }

    public Knight(Knight decorated) {
        this.decorated = decorated;
    }

    public void attackWithSword() {
        if (decorated != null) decorated.attackWithSword();
    }

    public void attackWithBow() {
        if (decorated != null) decorated.attackWithBow();
    }

    public void blockWithShield() {
        if (decorated != null) decorated.blockWithShield();
    }
}

Eve: So what we improved there?

Pedro: First af all we make class Knight instead of interface to have access to hit points. Then we provide two diferent constructors, default for standard behaviour, and decorated, which delegates call to decorated object.

Eve: Is it fair to use abstract class instead of interface?

Pedro: No, but we avoid two classes with similar behaviour and fill each decorated object with default implementation, instead of forcing to implement each of the methods.

Eve: Ok, what's about Power Armor?

Pedro: Easy as well

public class KnightWithPowerArmor extends Knight {
    public KnightWithPowerArmor(Knight decorated) {
        super(decorated);
    }

    @Override
    public void blockWithShield() {
        super.blockWithShield();
        Armor armor = new PowerArmor();
        armor.block();
    }
}

public class KnightWithAdditionalHP extends Knight {
    public KnightWithAdditionalHP(Knight decorated) {
        super(decorated);
        this.hp += 50;
    }
}

Pedro: Two decorators that fulfils FBI requirements, and we able to create super knight, with behaviour like Galahad, but super armor and 50 more hit points.

Knight superKnight =
     new KnightWithAdditionalHP(
     new KnightWithPowerArmor(
     new Galahad()));

Eve: Nice trick!

Pedro: You are welcome to show the similar behaviour in clojure

Eve: Here it is

(def galahad {:name "Galahad"
              :speed 1.0
              :hp 100
              :attack-bow-fn attack-with-bow
              :attack-sword-fn attack-with-sword
              :block-fn block-with-shield})

(defn make-knight-with-more-hp [knight]
  (update-in knight [:hp] + 50))

(defn make-knight-with-power-armor [knight]
  (update-in knight [:block-fn]
             (fn [block-fn]
               (fn []
                 (block-fn)
                 (block-with-power-armor)))))

;; create the knight
(def superknight (-> galahad
                     make-knight-with-power-armor
                     make-knight-with-more-hp)

Pedro: The same functionality.

Eve: Yes, just pay attention to power armor decorator.

Episode 22: Proxy

Deren Bart manages the system for making mixed drinks. It is rigid system, because after making a drink Bart must manually subtract used ingredients from the bar. Do this automatically.

Pedro: Can we get access to his codebase?

Eve: No, but he sent some APIs.

interface IBar {
    void makeDrink(Drink drink);
}

interface Drink {
    List<Ingredient> getIngredients();
}

interface Ingredient {
    String getName();
    double getAmount();
}

Pedro: Bart doesn't want us to modify sources, instead we need to provide some additional implementation for IBar interface with autosubtracting used ingredients.

Eve: And what are we responsible for?

Pedro: Implementing Proxy Pattern, I've read about it some times ago.

Eve: I'm all listening.

Pedro: Basically we delegate all existing functionality to standard IBar implementation and provide new functionality inside ProxiedBar

class ProxiedBar implements IBar {
    BarDatabase bar;
    IBar standardBar;

    public void makeDrink(Drink drink) {
       standardBar.makeDrink(drink);
       for (Ingredient i : drink.getIngredients()) {
           bar.subtract(i);
       }
    }
}

Pedro: They need to replace StandardBar implementation with our ProxiedBar.

Eve: Seems super easy.

Pedro: Yes, additional plus that we don't break existing functionality.

Eve: Are you sure? We didn't run regression tests.

Pedro: Everything we do is delegating functionality to already tested StandardBar

Eve: But you also substracts used ingredients from BarDatabase

Pedro: We assume, they are decoupled.

Eve: Oh...

Pedro: Does clojure have some alternative?

Eve: Well, I don't know. What I see here you are using function composition.

Pedro: Explain.

Eve: IBar implementation is a set of functions, and another IBar is another set of functions. Everything you talking about additional implementation could be covered by function composition. It's like make-drink and after that subtract-ingredients from bar.

Pedro: Maybe code is more clear?

Eve: Yes, but I don't think something special here

;; interface
(defprotocol IBar
  (make-drink [this drink]))

;; Bart's implementation
(deftype StandardBar []
  IBar
  (make-drink [this drink]
    (println "Making drink " drink)
    :ok))

;; our implementation
(deftype ProxiedBar [db ibar]
  IBar
  (make-drink [this drink]
    (make-drink ibar drink)
    (subtract-ingredients db drink)))

;; this how it was before
(make-drink (StandardBar.)
    {:name "Manhattan"
     :ingredients [["Bourbon" 75] ["Sweet Vermouth" 25] ["Angostura" 5]]})

;; this how it becomes now
(make-drink (ProxiedBar. {:db 1} (StandardBar.))
    {:name "Manhattan"
     :ingredients [["Bourbon" 75] ["Sweet Vermouth" 25] ["Angostura" 5]]})

Eve: We could leverage protocol and types to group set of functions as a single object.

Pedro: Looks like clojure has object-oriented capabilities as well.

Eve: Correct, moreover it has a reify function, which allow you to create proxies in a runtime

Pedro: Like class in a runtime?

Eve: Sort of.

{{{class="brush: clojure"
(reify IBar
  (make-drink [this drink]
    ;; implementation goes here
  ))

}}}

Pedro: Looks handy.

Eve: Yes, but I still don't understand how it differs from Decorator.

Pedro: They are completely different.

Eve: Decorator adds functionality to the same interface, and so does Proxy.

Pedro: Well, but Proxy is...

Eve: Even more, Adapter is not very different as well.

Pedro: It uses another interface.

Eve: But from implementation perspective all these pattern are the same, wrap something and delegate calls to wrapper. "Wrapper" could be a good name for these patterns.

Episode 23: Bridge

Girls from HR agency "Hurece's Sour Man" trying to identify candidates to their open job positions. The problem is jobs often created by customers, but requirements to the jobs are developed by HR. Provide them with a flexible way of collaborating.

Eve: I don't understand the problem.

Pedro: I have a bit of background. They have a very strange system which defines job requirements as an interface.

interface JobRequirement {
    boolean accept(Candidate c);
}

Pedro: Every specific requirement implemented as a new subclass of this.

class JavaRequirement implements JobRequirement {
    public boolean accept(Candidate c) {
        return c.hasSkill("Java");
    }
}

class Experience10YearsRequirement implements JobRequirement {
    public boolean accept(Candidate c) {
        return c.getExperience() >= 10;
    }
}

Eve: I've got an idea.

Pedro: Take into account, this requirement hierarchy is designed by HR Department.

Eve: Ok.

Pedro: And they have a Job hierarchy, where each specific job is subclass as well.

Eve: Why do they need a class for each job? It should be an object.

Pedro: The system was designed when classes were more popular than objects, so live with this.

Eve: Class were popular than objects?!

Pedro: Yes, listen and don't interrupt me. Jobs with requirements is completely separate hierarchy, and it is developed by customers. We introduce pattern Bridge to separate these two hierarchies and allow them live independently.

abstract class Job {
    protected List<? extends JobRequirement> requirements;

    public Job(List<? extends JobRequirement> requirements) {
        this.requirements = requirements;
    }

    protected boolean accept(Candidate c) {
        for (JobRequirement j : requirements) {
            if (!j.accept(c)) {
                return false;
            }
        }
        return true;
    }
}

class CognitectClojureDeveloper extends Job {
    public CognitectClojureDeveloper() {
        super(Arrays.asList(
                  new ClojureJobRequirement(),
                  new Experience10YearsRequirement()
        ));
    }
}

Eve: So where is the bridge?

Pedro: JobRequirement, JavaRequirement, ExperienceRequirement is one hierarchy, yes?

Eve: Yes.

Pedro: Job, CongnitectClojureDeveloperJob, OracleJavaDeveloperJob is another hierarchy.

Eve: Oh, now I see. A link from Job to JobRequirement is a bridge.

Pedro: Exactly! This is how HR can use this system to find candidate matches.

Candidate joshuaBloch = new Candidate();
(new CognitectClojureDeveloper()).accept(joshuaBloch);
(new OracleSeniorJavaDeveloper()).accept(joshuaBloch);

Pedro: Here is the point. Customers use Job as abstraction and JobRequirement as an implementation. They just create a job with descriptions or so, and HR is responsible to convert these descriptions into specific set of JobRequirement objects.

Eve: Got it.

Pedro: So as far as I understand, clojure could mimic this pattern by using defprotocol and defrecord?

Eve: Yes, but I want to revisit the problem.

Pedro: What's wrong?

Eve: Here is we have a static flow: customer create job positions, human resources convert job position to a set of requirements and run a script against their candidates database to find a matches.

Pedro: Correct.

Eve: So there is already dependency, HR can't do anything without open job positions.

Pedro: Well, yes. But they can develop a set of structured requirements without knowing what open positions might be.

Eve: For what purpose?

Pedro: Later this can be reused by Job creators, so HR avoid doing the same work twice.

Eve: Okay, got it, but this problem is artificial. Basically what we need is a way to colaborate between abstraction and implementation.

Pedro: Maybe, but I want to see your clojure way solution for that specific problem using bridge pattern.

Eve: Easy. Let's use adhoc hierarchies.

Pedro: For abstractions?

Eve: Yes, jobs hierarchy is abstraction, and people need just to enhance hierarchy.

;; abstraction

(derive ::clojure-job ::job)
(derive ::java-job ::job)
(derive ::senior-clojure-job ::clojure-job)
(derive ::senior-java-job    ::java-job)

Eve: HR department are like developers, they provide implementation for this abstraction.

;; implementation
(defmulti accept :job)

(defmethod accept :java [candidate]
  (and (some #{:java} (:skills candidate))
       (> (:experience candidate) 1)))

Eve: Later, when new jobs are created, but requirements are not yet developed and no accept method implementation for this type of job, we fallback using adhoc hierarchy.

Pedro: Hm?

Eve: Assume someone created a new ::senior-java as a child of ::java job.

Pedro: Oh, and if HR not provided accept implementation for dispatch value ::senior-java, method with dispatch value ::java will be called, yes?

Eve: You learning so fast.

Pedro: But is it real bridge pattern?

Eve: There is no bridge here, but abstraction and implementation can live independently.

The End.

Cheatsheet (instead of conclusion)

It is very confusing to understand patterns, which often presented in object-oriented way with bunch of UML diagrams, fancy nouns and exist to solve language-specific problems, so here is a revisited mini cheatsheet, which helps you to understand patterns by analogy.

  • Command - function
  • Strategy - function, which accepts function
  • State - strategy, depends on state
  • Visitor - multiple dispatch
  • Template Method - strategy with defaults
  • Iterator - sequence
  • Memento - save and restore
  • Prototype - immutability
  • Mediator - reduce coupling
  • Observer - function, which calls after another function
  • Interpreter - set of functions to process a tree
  • Flyweight - cache
  • Builder - optional arguments
  • Facade - single point of access
  • Singleton - global variable
  • Chain of Responsibility - function composition
  • Composite - tree
  • Factory Method - strategy for creating objects
  • Abstract Factory - strategy for creating set of related objects
  • Adapter - wrapper, same functionality, different type
  • Decorator - wrapper, same type, new functionality
  • Proxy - wrapper, function composition
  • Bridge - separate abstraction and implementation

Cast

A long time ago in a galaxy far, far away...

With the lack of imagination all characters and names are just anagrams.

  • Pedro Veel - Developer
  • Eve Dopler - Developer
  • Serpent Hill & R.E.E. - Enterprise Hell
  • Sven Tori - Investor
  • Karmen Git - Marketing
  • Natanius S. Selbys - Business Analyst
  • Mech Dominore Fight Saga - Heroes of Might and Magic
  • Kent Podiololis - I don't like loops
  • Chad Bogue - Douchebag
  • Dex Ringeus - UX Designer
  • Veerco Wierde - Code Review
  • Dartee Hebl - Heartbleed
  • Bertie Prayc - Cyber Pirate
  • Cristopher, Matton & Pharts - Important Charts & Reports
  • Tuck Brass - Starbucks
  • Eugenio Reinn Jr. - Junior Engineer
  • Feverro O'Neal - Forever Alone
  • A Profit NY - Profanity
  • Bella Hock - Black Hole
  • Sir Dry Bang - Angry Birds
  • Saimank Gerr - Risk Manager
  • Deam Evil - Medieval
  • Podrea Vesper - Eavesdropper
  • Deren Bart - Bartender
  • Hurece's Sour Man - Human Resources

P.S. I've started to write this article more than 2 years ago. Time has passed, things have changed and even Java 8 was released.