Don't read Design Patterns

home

 

Once you learn basic coding and start to write larger programs, it seems there must be some advanced concepts. If you use the internet, "Design Patterns" seems to be the most popular. People who like them really like them, but the explanations and examples seem very intimidating.

This is an explanation of how they fit into computer science, what you should be learning instead, and explanation of what each Design Pattern is trying to say.

Very short version

Design Patterns are tricks to organize a very large program in a pure Object Oriented style. If you're having problems doing something, they won't help. If you're not trying to use a pure OOP style, they'll be awkward (if you have to ask whether you're trying to use a pure Object Oriented style, you're not.)

Some things a normal second semester of Com Sci, would teach (which are not Design Patterns): using loops to do common things with arrays. Nicer versions of arrays. Using linked-lists to insert anywhere, or hash-tables for Cost["hammer"]=5.97;. Using 2D square and "ragged" arrays. Becoming more comfortable writing small classes with useful constructors, helper member functions, and passing/returning them in functions. Storing data in arrays of classes containing arrays of classes. Using pointer-style variables. Pointers to functions used as "plug-ins". Recursion. Becoming more comfortable with polymorphism and dynamic dispatch and the Animal a=new Cat(); trick. Big-O to analyze speed.

Short Version

From about 1995 to 2000 the internet went public and grew into the first dot-com boom. There was a huge demand for web Java programmers: Object Oriented Programming was the new promising style, and Java was the hot new OOP language.

Meanwhile, in 1994, a book was written named "Design Patterns: elements of reusable object oriented software." It was two dozen programming tricks renamed and rewritten in Object Oriented style. Nothing special, but nothing wrong with it either.

The flood of new internet Java coders were mostly self-taught and didn't feel connected to traditional computer science. They were looking for guidance and that book was exactly what they wanted. Pretty soon employees were using the official Design Pattern names in meetings. More importantly, overwhelmed managers realized they could ask Design Pattern questions during performance reviews and for job interviews. At that point, whether they worked or not, Design Patterns were a part of the corporate Java world (and now its copy, C#).

Design Patterns today are those same two dozen tricks from that book, with very minor changes.

Software engineering

Software engineering is everything to do with managing and organizing programming projects. The Design Pattern book was originally a paper at a Software Engineering conference, because the authors were software engineering professors. A scattering of things in that field:

You'll see plenty of "Why use Design Patterns" internet essays which somewhat cover that last set of items. For example, clearly programmers should be able to recognize common arrangements. Suppose 2 program parts talk to each other through two middlemen. That's a thing, and it's usually "my assistant talking to your assistant". It would be even cooler if we had a word for that, and could use it in a comment. But those essays don't tell you how Design Patterns will help.

There's a well-known software engineering essay by Fred Brooks, an old-time IBMer, "The Mythical Man-Month" and a follow-up "No Silver Bullet". They outline our quest to make programming not be so hard. The name of the first is about organizing teams - if a project was 3 months behind schedule you could add 3 coders to get 3 extra "man months". That never worked. The name of the second comes from the frustration that there has to be something, a silver bullet, that makes big coding projects less messy. Object Oriented Programming was one of those hopes.

 

Object Oriented Programming

Design Patterns are purposely strongly Object Oriented. OO means different things, but at the time it meant using classes and polymorphism for everything you possibly could. Here's the idea, going back a-ways:

The very first programs had only a "jump to any line" command. Clever programmers could use it to do anything they needed. Eventually two common jumping patterns emerged: a "do this or that" and a "repeat these lines while a condition is true". As you might guess, that's how we invented if/else's and while loops.

We eventually had a radical idea. What if we quit using jumps, and only used if's and loops? They got the job done, your program was a little bigger and slower, but sooo much easier to read and debug. Older coders mocked the idea, but it worked - we could write much more reliable programs, faster.

Before jump-elimination (called Structured Programming), we thought the secret to writing better programs was adding more features to computer languages. The lesson we learned with jumps was that limiting to a standard, simpler way could also have big payoffs.

In the 1990's we thought we had something similar. We had classes and polymorphism, but we used them as needed, mixing in all sorts of other tricks. OOP's idea was that limiting ourselves to only communicating classes might be another big gain through simplification. Hopefully programming would be merely arranging classes, using a UML diagram, full of helpful tools and warnings. It didn't work out. OOP programs take about as long, with about as many bugs, as anything else.

The Design Pattern book was written at the start of that. Much of it was about adapting old tricks into classes and inheritance. It wasn't a grand plan or anything. At the time OOP had lots of buzz, and "how would you make an OOP version of trick X?" seemed like a pretty cool topic.

Things to note about Design Patterns

A summary and some new things:

Solid snake

Mixed in with design patterns you often find the acronym SOLID. The exact names for them (S in "Single Responsibility Principle") are old and awkward and are sort of made up to get S, O, L, I and D. But they more-or-less are about the basics of communicating encapsulated classes. Plus, since they're so old, there's some fun history to them:

The "L" is about the real way to use inheritance. Suppose you need a Cat class in one place, and a Dog class in another. You could make them using inheritance from Animal, but there's no advantage. The real purpose of inheritance is to make Animal a; point to either a Cat or Dog. Or, better an array of Animals holding a mixed list of both. You'll be using virtual functions, which need to work in a consistent way for each subclass.

D is just a way to say encapsulation. But the phrase, Dependency Inversion, involves some cool history. In old-style programming the main program needed to know the exact layout of the data and worked directly with it for maximum efficiency. A slow, barely-any-memory computer had to be able to run it. The modern data-hiding method is "inverted" from that. It's a neat example of how many old computer tricks were the best at the time. Moore's Law, thousands of times more speed and memory, made OOP and other ideas possible.

O is a rule about how to gradually grow a program. In theory, you plan it all out and write it. But for real there are always things crammed in later. O says if you have a written, tested part, already used in a few places, don't try to jam in new features. That has a chance to break it for everyone using it the old way. Instead, add the new features in a new class containing the old one, possibly through inheritance.

S says to break the program into small, independent classes. Obvious now, but 20 years ago that was interesting. It says each part should do one thing, but that's not terribly helpful, since you can define "one thing" however you like.

I says not to make interfaces too big, which is that same thing as S. But we needed an I, otherwise it would be just SOLD. It's also some fun history. Now, an interface is the public functions of a class. But before that it meant just any list of functions. This was before we even grouped things in namespaces. There was a time when "don't make huge interfaces" was interesting advice.

 

To re-sum up, if you read how you should learn "SOLID", well, I guess. But you probably already know it. And if not, read about encapsulation and basic object-oriented design. Because if you study and decipher SOLID, that's all it is.

 

Explanation of each Design Pattern

Officially, the things in that one book are the real design patterns. But there are a few things it describes and says aren't design patterns, which people now say are.

When the book was written, Java and C# didn't exist. The examples tend to be in C++ or SmallTalk (an experimental OOP-like language like nothing you've seen). Each trick has about one example. If you look around on the internet, you'll often see the same example.

The opening descriptions of each trick have some odd phrasing and words that might seem peculiar, now. Knowing how to read them and understand the actual meaning seems as if it might be useful, but it's a lot of work and not all of them completely make sense. So I didn't try, and just explained them in regular english. If you want, you can read the official descriptions and example and try your luck.

Facade, Adaptor

These are two names for replacing an interface. Often you have a class that does everything you need, but want to change the functions. Add a fake class with the functions the way you like them, using the original class to do all the work. That's very common and called a wrapper. The books breaks wrappers into two types: a Facade as when you want to change the functions, and an Adaptor as when you have to. But I don't think those new names caught on.

Making one is a trick. You can't simply inherit from the thing you want to change, since that won't let you hide or rewrite existing functions. Instead, the wrapper uses the original class as it's only variable. Here, DogWrap wraps Dog. It changes getKilos to getPounds, but leaves getName the same:

class DogWrap { // wrapper on Dog
  Dog d; // does all the work

  string getName() { return d.getName(); } // a "pass-through"

  // change getKilos with getPounds:
  float getPounds() { return d.getKilos()*poundPerKilo; }
}

Notice how we had to write getName as a do-nothing function, running the original getName with no changes. To use a wrapper (this is a little advanced and a little boring), we'd write a constructor taking a normal Dog:

  // the constructor inside of DogWrap, requires an input Dog:
  public DogWrap(Dog do) { d=do; }
  
Dog d; // pretend we have this, but don't like Dog functions
d.getKikos(); // yuck
DogWrap dd = new DogWrap(d);
dd.getPounds(); // our new, improved function

Proxy

This is a normal computer term which wasn't changed when they put it in the book. It means to add an interface or wrapper that does some buffering. It's purpose is speedier execution.

For example, a typical file-reading class lets you read 1 line at a time from disk. But, for speed, it really grabs a full disk page into a buffer. When that runs out, it reads the next page. Sending commands to a graphics card also runs through a proxy. You think you're running commands, but it's really saving them in a buffer. When you finish, it reorganize the commands for speed, then runs them for real.

Inversion of Control

This one is so obsolete that it's fun to read about. It means having things like OnButtonClick - callback functions on GUI controls.

20 years ago programs printed "Enter name:" and waited, like in the movie War Games. There were no GUIs or web forms. The program knew and controlled what input was coming next. When we we able to put inputs as text-boxes with buttons, it was like the inputs were controlling us. Control was inverted! But now that's just a "back in my day" joke.

Today, IoC is sometimes used for anything that helps you make a GUI. For example Visual Studio Forms lets you drag in a button and double-click it to make the OnClicked call-back. It's an IoC framework, sort of.

Composite

This is another name for a tree. The reason for this, I think, is another fun story about computer history.

There are two general cases for tree use. One is for optimized data storage - maybe you could store numbers in a simple list, but a hash table, or a tree might run a little faster. And there are lots of types of trees with math explaining which to use when, for speed. These trees feel very old-school data-first-y. Proper OOP should hide those details inside a class.

The other case for trees are things which are logically trees. File directories, or robots made of blocks snapped into other blocks. The most computery sort of trees are math expressions. This tree represents 4*(5+3). You should be able to see how 5+3 goes first:

   *
 /   \
4     +
     / \
    5   3

I think saying "Composite Pattern" is a way to say you're making a tree for the second "good" reason.

To use trees you need to know recursion, since trees are a recursive data-structure. In fact, we usually learn recursion first, then learn how trees work by writing depth-first then breath-first (and pre-order, in-order ...) recursive traversals.

Decorator

This is one is if you want to be fully Object Oriented. It's not very useful otherwise, but it's a cool puzzle using inheritance. It's a way to modify class functions on-the-fly.

Suppose we have a Dog class with an eat function. We want to change how it eats, change it back, and so on, while the program is running. Those new ways of eating aren't options in the Dog now, and we know we shouldn't rewrite it to add them. Maybe we don't even "own" Dog, so couldn't expand eat even if we wanted to.

Our plan is to use the wrapper method. We'll make a wrapper that changes eat to be slow, another making it fast, another to bark softly, and so on. To make it eat fast and also bark soft, we'll add both wrappers (one over the other, which is the only possible way). But we have a problem - our old system won't let us wrap a wrapper. Wrappers can only go on Dogs, and wrapped Dogs aren't Dogs any more.

To let wrappers wrap Dog and other wrappers, we'll spit into an abstract Dog class. Real Dogs are a Dog subclass, and so are all wrappers:

class Dog { // abstract Dog
  virtual string bark();
  virtual void eat(Food f);
}

class BasicDog : Dog {
  // the real dog variables and functions
}

// normal dog:
Dog d = new BasicDog();

The wrappers are the same as normal, except they inherit from Dog. This one makes us eat slow:

class EatSlowDog : Dog { // <- inherits from abstract superclass Dog
  Dog d; // will probably be a basic Dog, or else another wrapper
  
  string bark() { d.bark(); } // pass-though

  void eat(Food f) { new slow-eat code here }
  
  // constructor:
  public EatSlowDog(Dog oldDog) { d=oldDog; }
}

Now d=new EatSlowDog(d); makes d eat slow. d=new BarkSoftDog(d); adds a second soft-bark wrapper. A crazy thing, to "take off" slow eating, we can't remove that wrapper. We need to add a third eatNormal wrapper. That's a bizarre, horrible inefficient way to do it, but it works and is very OOP.

Decorate is also a normal computer science term. It's a reference to decorating a holiday tree - it means adding some temporary information to a class. Suppose Dogs have an extra general purpose int, and you temporarily use it to rate them for adoption by one family. We'd say we're decorating the Dogs with that rating.

Dependency Injection

This is about the standard way we like to create and hook together the classes that make our program. We want them to be able to talk directly amongst themselves, but also be independent. The way we do that is with function pointers and abstract interfaces.

Suppose we have a menu controlling a group of rabbits. We know that nothing in the menu class actually mentions rabbits. OnButtonClick's let us reach out to the rabbits, or wherever else we need them to go. Menus can talk to rabbits, while being independent of them.

We'd like the menu system to be able to check the input for itself, but input can come from mice, finger taps or VR gloves, with a different input class for each. We'll use the same trick with plug-ins. Menus get a dangling isNewCick() function.

Finally, suppose we have a bunch of cool menu animation sub-systems. It seems safe to have the menu class directly know about them, and create the standard one. But we're creating more animations all the time, which would need to be added to the list. Constantly changing the menu class is a buggy pain, so we'll use more plugs-ins for those. Menus will know the animation functions, but won't know about any specific animation systems.

This is a pretty neat system, except classes have no ability to connect themselves, or to make any subparts they need. The main program, at the start, needs to create and connect everything. That's a ton of work, but there are some tricks. We can have the menu's constructor require everything it needs as inputs. That's a nice way to be sure we don't forget.

More extreme than that, we could make our program with a GUI. It will let us select and drag the parts we want, save the connections, then automatically write the code to create and connect them all at start-up. Those are sometimes called DI frameworks (DI, for Dependency Injection).

The exact term DI is muddy. Injection isn't a computer word. Dependency is, but it doesn't mean the other things you use. A dependency is what your class directly mentions. If you put your class into a new program, dependancies are the "can't find XYZ" errors. Our plan of using function pointers to communicate allows us to eliminate most dependencies. But plenty of terms mean what they mean just because. When you hear DI, it's something to do with the general system of loosely connected classes, needing to be connected by a master program.

I wrote you use plug-in functions. But an abstract class is the same thing and is often nicer. They're more Object Oriented, and group everything together. We might connect to real input classes using this (remember, there's no code - just a list of functions):

class abstractInputSytem {
  bool hasNewClick();
  Point getClickPosition();
  ...
}

Now our menu class has a real dependency, on abstractInputSystem, but that's good. It's a nice built-in reminder, with compiler errors to force us to look at the functions we need to supply.

Visitor

This is a another Object-Oriented puzzle for a complicated but common problem. Suppose we loop through an array of Animal's, filled with Cats and Dogs, and want to run a function that works differently for each. That's what virtual functions do. But the class is finished - we can't add our new function to it.

An easy way to fake a virtual, which is not Visitor, is to manually check the subtype. This runs a different petRating for Cats and Dogs:

for(int i=0; i<A.Length; i++) {
  print( petRating(A[i]) );
}

int petRating(Animal a) {
  // check real animal type:
  Cat c = a as Cat;
  if(c!=null) return .. ; // insert math for cats
  Dog d = aa as Dog;
  if(d!=null) return .. ; // insert math for dogs
  ..
}

That works, but isn't proper Object Orientedness. The Visitor Pattern is a complicated way to do the same thing. Why and how it works shows off some cool things, but don't try to use it. First, let's rule out easy ways of doing it. It seems like we could use overload functions:

int petRating(Dog d) { return .. } // math for Dog
int petRating(Cat c) { return .. } // math for Cat

for(int i=0; i<A.Length;i++)
  petRating(A[i]); // opps. Error.
  // Trying to call non-existent: petRating(Animal a)

The problem is, overloaded functions are just normal functions. They don't have the ability to check the real subtype. Only a member function like A[i].petRating() can do that.

If you know about C#'s extension methods trick, which lets anyone outside of the class add fake member functions, that won't work for the same reason - they're normal functions. a1.eat(5) is turned into eat(Animal a1, 5), which again, won't check a1's real subtype.

Hopefully that was education. And now we're ready for the Visitor pattern. First we decide to group our new Cat and Dog functions into one class, under an abstract superclass:

// generic slots for 1 cat function, and 1 dog function:
class AnimalVisitor {
  virtual int catIntVisit(Cat cc);
  virtual int dogIntVisit(Dog dd);
}

// real functions, written as a subclass:
class petRating : AnimalVisitor {
  // overrides:
  public int catIntVisit(Cat c) { return 3; } // Cat rating Math
  public int dogIntVisit(Dog d) { return d.weight*2; } // Dog rating Math
}

// sample use:
AnimalVisitor av = new petRating();

This is total class abuse. Notice how the "class" petRating doesn't even have any variables. It's just a fancy way to hold two functions. Whenever you need to plug in different ones, you need to write a new sub-class with them.

Now we'll go to the other side. When we write the class, we need to plan ahead to let people use Visitor. The Animal class has one function taking an abstract AnimalVisitor as input:

class Animal { // base class simply establishes the function:
  ..
  virtual int receiveVisit(AnimalVisitor av);
}

Remember, the real av will always be hand-written cat and dog functions. Cats will need to pick out the cat function, same for Dogs. They do it by overriding receiveVisit:

class Cat : Animal {
  ..
  // if you give a Cat an AnimalVisitor, it runs the cat fuction:
  int receiveVisit(AnimalVisitor av) { return av.catIntVisit(this); }
}

class Dog : Animal {
  ..
  // dogs run only the dog function:
  int receiveVisit(AnimalVisitor av) { return av.dogIntVisit(this); }
}

That's the whole thing. Here it is being used altogether, so we can see how it works:

// av holds the the new Cat and Dog functions we wrote:
AnimalVisitor av = new petRating();

for(int i=0; i<A.Length;i++)
  total += a1.receiveVisit(av); // <- gets the rating, whether a1 is a cat or dog

The payoff is having only a single line in the loop. The check for a Cat or Dog is done automatically by the virtual receiveVisit function, which feels very Object Oriented. The whole thing, for a Cat: a1.receiveVisit(av) runs the cat version, which becomes av.catIntVisit(a1), which finally runs the function we wrote in petRating for cats.

So, yikes! But it's kind of neat to see how this works, and how nothing simpler would have (unless you cheat and use if's, which is way easier). Real ones are actually worse than this. They use the same name for every function (where I wrote catVisit and dogVisit, they use visit). That's fine because of overloading, but harder to read.

The main reason to know this is if you see a premade class with a member function like Accept(Visitor). You'll know it's for the visitor pattern. But the thing to know is that Visitor doesn't do anything you can't already do. "if a1 is a Cat" works fine, even if OOP people make fun of you.

Interpreter

This is making a mini-language, then writing code to read it. As a crude example, a shopkeeper in your game might say: "$charName$, welcome to $cityName$". Your program would know to substitute the right words. Or, a similar idea, the AI for a guard might be written "W[3,4]P(8)W[0]", which means it walks to station 3 or 4, pauses for 8 seconds, then walks back to station 0. You'd write code that knows how to read that, then you have an easy way to make paths.

It's an old trick, perfectly fine. It's not especially OOP, but not not OOP either. And Interpreter is a real computer word for a program which reads and directly runs some code, so it's being used correctly here.

Singleton

This means using a class to hold your global variables. The advantage is being able to easily swap in, sometimes temporarily or for testing, different sets.

Instead of writing your globals are globals, make a class with them all as public. Then, before the program starts, make one instance, which everyone will share. They act like global variables. Usually, so they can all find it, we have one real global variable pointing to it. Often it's a static class variable with a name like theOneRealInstance. To look up the global totalCats, use GlobalCatVars.theInstance.totalCats.

The only tricky part is set-up. Before doing anything else, someone has to make one instance of your globals class, connect it to the real global pointer, and never replace it by accident with a new one.

A neat trick is, despite the name Singleton, you can have multiple copies of your "global variables" class. Pick which one is active by changing the oneRealInstance pointer. You might have one copy for testing, or suppose a game lets you walk between levels. You could have "globals" for each level.

Observer

This isn't an actual trick, and it's about speed, not organization. The situation is you have one class displaying some data, maybe a pie chart. Another class holds the data, sending you a redraw command after each change. If there are a dozen changes all at once, you get told to redraw dozens of times, when once at the end would be better.

The Observer Pattern is just some suggestions - maybe don't worry about the extra redraws, or maybe the person making the changes needs to call for the redraw, and so on. A funny thing, it's not a design trick. It's a typical speed trick that makes the program more complicated.

The normal word for a "notify me of changes" class is a Listener. Generally a Listener needs to know about and react to every single thing that happens. So an Observer, I think, is a type of Listener.

Service Locator

This is when someone gives you something that lets you find globals. Often instead of just variables, they're big things like databases, so we call them a service. But they're still globals.

In a sense, giving the globals to you gets rid of globals. A global is something extra that just needs to be in your environment. Giving you the globals means they're just another input. We're no longer using them through irregular channels, and we can switch them around as needed.

State

This is another Object Oriented way of doing something we can already do just fine the normal way. It's about using State Machines.

If you haven't seen the State Machine trick, it's using a variable to remember what you were doing (what state you're in.) We can use an int variable, but it's nice to use an enumerated type:

enum JumpStates {waiting, launching, soaring, landing};
JumpStates js=waiting;

Our code uses js to decide what to do, changing it as needed:

if(js==waiting) { if(spaceKey) js=launching; force=0; }
else if(js==launching) { 
  if(abortKey) js=waiting;
  else { force+=t*0.4; if(force>5) js=soaring; } // lift-off speed is 5
}
else if(js==soaring) { // and so on
...

You'll also see pre-made systems to make that easier, or GUI's where the states are circles with arrows showing when to change from one to the other. Those are just normal, and aren't the State Design Pattern.

The State Design Pattern uses classes and a virtual function to make it more OOP. We make an abstract State class with a sub-class for each real state:

// abstract class with 1 function:
class JumpState { virtual doAction(); }

// subclasses for each state 
class Waiting : JumpState { 
  void doAction() { if(spaceKey) ... }; // same code as before
}

class Launching : JumpState { 
  void doAction() { // same code as before
   force+=t*0.4;
   if(force>5) backpointer.js=new Soaring();
  }
}

The reason for this is so our main loop can be just js.doAction();. Since js is an abstract super-class, that line look up the real subclass, and run the correct code. We eliminated the if's.

We change our state by creating a new instance and assigning it to js. In other words, js starts as really being a Waiting. When they press space, we toss that away and replace it with a Launching. js.doAction(); now runs the Launching code.

If nothing else, it's another example of virtual functions and class abuse.

Iterator

This one is a specific low-level trick and is obsolete, sort of, since Iterators are now built into many languages. They're a way to write a loop which works on any type of list - one function can take an array, or a fancy array, or a linked list. For example:

int countSome(iterable L) {
  iterator iter=L.getIterator();
  while(!iter.done()) { // use the iterator to walk through
    if(iter.value() ..
    ..
    iter.next();
  }
}

All lists inherit from abstract class iterable, so L could be an array, for example. The new functions next, value and done are written based on what you are. Array-type lists have a hidden i, going up by 1. Linked-list-style lists have hidden node pointers. iter.next() automatically does whatever it needs to do for that list.

Writing a function that works for any type of list is kind of neat. But there aren't that many types. And we often choose different types of lists because we want to do things in a different way for each.

MVC

This is the idea that any program that displays things can break itself into a data-only part, and a display-only part. It stands for Model, View, Controller (model is the computer word for data). Controller means that instead of having the data and viewer talk to each other, we'll have them managed by a master program.

This is a very old one. Way back, the "view" was just a printing results on paper. As we were able to write graphical systems, a data/view split was a new idea. Now, it's just obvious.

You'll often see MVC added to names of program-making GUIs. Microsoft's MVC web-page designer has you describe the inputs in one place, how they should look in another and builds the real HTML from those. Web style-sheets are about the same idea, except no one calls those MVC.

Factory/Abstract Factory

These are a repeat of loose-coupling: use an abstract interface, with plug-in concrete subclasses. But the trick is now being used to create new object, so it feels different:

// TabbyMaker is a real subclass of abstract CatMaker:
CatMaker catFactory = new TabbyMaker();
 ..
Cat c1 = catFactory.makeCat();  // virtual function, creates a Tabby
Cat c1 = catFactory.makeLeash();  // virtual function, leash for a Tabby

Factories often need to create several related items which need to be in synch - Tabbys and leashes fit for Tabbys. A function for each is an Abstract Factory. A regular Factory has one function, but allowed to have a "make this type" input:
catFactory.make(itemType.leash);.

Factory caught on as a general term for any set-up where you can plug-in the things to make, with a nice function to make them.

Prototype

This is for when your program needs to create complicated items, but in only a few settings. For example, 8 types of Tetris-like puzzle pieces. It says to get a master list with every item, then make the ones you need by copying.

That should sound cheaty. Sure, copying something big is much easier than writing a function to hand-create it. But someone had to make the original. Prototype is for when the original is somehow free. Maybe another program gave you the master list. Or, more commonly, you're using a puzzle-game-making program, with a GUI to pre-make pieces: pre-make 8, put them in a list, and your program merely needs to run "copy" to get any piece.

Builder

This is the idea of creating a complex object in 2 stages, using some sort of description format. Suppose our program now needs to make any sort of puzzle piece, on the fly. We can create rules for a color-by-numbers grid. Then piece-creation involves filling that in, and sending the result to the creation function.

You might think, well, JSON - any program can describe an object in JSON, then send it to be created using built-ins. But that format isn't easy to work with. The Builder trick is about making creation simpler by making a custom easy-to-use middle step based on the exact problem.

It's really a version of a general trick from algorithms. Take a messy problem and transform it another which is basically the same, but easier to think about.

Flyweight

This one is mostly obvious. If you have data from 2 or more tables, save it using those tables. For example, say you have a list of people with personal info, including their country, and you want to be able to show their flag, national anthem and so on. Obviously, each person has a place for country code, using a separate country list.

The non-flyweight method would have one table, where each person had a copy of everything about their country. It seems as if no one would do that, but it can sneak up on you. Suppose you have 8 types of game pieces with 6 permanent stats (whether they can fly or swim). You decide to use the Prototype pattern to create the real pieces so you add variables for position and color (things that can change for each piece as you play.) Now every real cow piece has identical copies of the same 6 basic cow stats.

The only problem is that we're wasting memory. We could split the cows into 2 tables: basic cow stats, and actual cows. The real cows would have a link back to "basic cow" in the first table.

This trick is also the normal way to factor a relational database. I think it's in the book and called FlyWeight because of OOP vs. data-driven again. If you organize your data-structures first, old school, making cross-referenced tables is completely obvious. But, I suppose, not so much if you're thinking of class structure first.

Chain of Responsibility

This means what it sounds like, but can be a little fuzzy in practice. The most common example is writing a function with a plug-in "fallback" function if the first can't get the job done, which would have another possible fallback, and so on (it can be as long or short as you like). It might be for things expected to sometimes not work, like getting free space, or finding a closely-matching font.

It's fuzzy, since a function that purposely checks A, B, C and D wouldn't be considered using CoR. Neither if it called A which called B and so on. That's just normal programming. I'd assume CoR is a situation where it feels like "huh, I don't know, but I have this number for someone else who might help".

Command

This is the idea that instead of calling another function, you can create a command and send it to them. Instead of calling button1.hide() you might create command [obj: button1, action:hide] and send it to the menu system.

Advantages are being able to save commands, maybe for an undo system or a playback. It also allows looser coupling - you don't need to know specific functions in the other class (you need to know about the command class, but it's probably more generic). An obvious drawback is the extra complexity of making them.

But the original book also gives a second, completely different meaning. Command can also mean using a simple callback. OnButtonClick is a use of the Command Design Pattern. You're filling in the command for that button. I think the book is saying that, 20 years ago, real people used the word Command for both things (probably not the same people), and they were merely describing it.

Bridge

This is another one that solves a specific Object Oriented problem. It says, suppose you have, say, an airplane with several options for engines, navigation and so on, and each type requires different hand-written code (in other words, they aren't just changing a few numbers). Make an airplane with slots holding an abstract class for each: abstractEngine and so on. Make each real engine a subclass, and plug it in. Sometimes we call that style "composition".

That seems so normal, you have to wonder what the old way is. According to old-style OOP theory, you need to create a subclass of airplane for every possible combination. If you have 5 types for 3 parts, you need to write 125 subclasses (names like turboCompassSteelAirplane). At the time, in the OOP world, this was a serious thing, which people though about how to solve.

So why is using plug-in sub-parts called a Bridge? The official book say it's to "use an abstraction to decouple from the implementation". That's merely the definition of a wrapper. Bridge is just saying that we might want to cover up how it's really a composite. But today, most people are comfortable without, "airplane has an abstractNavigation member? Cool, I can use that".

Strategy

This is another word for using pointers to functions.

We can think of functions pointers in two ways. A callback is telling someone else what to do - OnButtonClick. A plugin asks someone else for help, so we can finish out job. There's no actual difference, but they feel different. Strategy is the word for a plug-in type function pointer. The idea is, suppose a monster is deciding who to attack, A function that rates each player could be plugged in, depending on the type of monster, to determine it's "strategy".

The OOP way of doing this is creating a class holding the function. that lets us bundle some variables with it. Our function can carry around some settings, or have a memory. If you haven't seen that trick, here's an example:

We want to be able to plug in any function to check whether we like certain names. Since we'll be plugging in classes, the variable needs to be an abstract super-class:

abstract class nameChecker {
  virtual bool checkName(string w);
}

Instead of a function pointer, anyone using this will have a nameChecker and call the only function:

nameChecker checkCatName; // insert a real class here
    
if(checkCatName.nameChecker(w1)) // say we like it
else if(checkCatName.nameChecker("Mr. "+w1)) // else try to fix

To make a real checker, inherit from nameChecker, write the real function, and add any variables you need. notThisLetter rejects names having one particular letter, which you can specify in advance. An extra class variable saves it. It also uses a "remember" variable, to let through every other bad name:

class notThisLetterEveryOther : nameChecker {
  char wrongLetter;
  bool freePass=false; // for fun, reject only every-other with that letter

  // constructor requires you give it the letter to skip:
  public notThisLetter(char notThis) { wrongLetter=notThis; }

  // reject words with the letter, half the time:
  virtual bool checkName(string w) {
    if(!w.contains(wrongLetter)) return true; // no bad letters
    
    // reject, but only every other:
    if(freePass) { freePass=false; return true; }
    freePass=true; // since we're rejecting this, next one gets a free pass
    return false;
  }
}

As usual, we create this and attach it to our abstract variable. Then we call the function as needed:

nameChecker checkCatName;
// cat names with z's are stupid:
checkCatname = new notThisLetterEveryOther('z');

if(checkCatName("biff")) // true
if(checkCatName("zillz")) // false
if(checkCatName("zarcy")) // true (free pass)

With just a function plug-in, we wouldn't be able to save that z, or remember every-other (but we could if the function had something called a closure, which saves variables with a function).

Mediator

This is another word for a manager or controller.

The interesting idea is it doesn't have to be for the entire program, like an old-style Controller. If you have a few parts that need to work together, but it's a pain having them all know about and talk to each other, you can make one special-purpose class that runs them all. It calls part A, passes the info to part B, and so on. The rest of the program only deals with that controller. But, really, you're simply making a new class using old ones, which is no big deal.

Memento

This is a very specific thing, and very minor, about making a system for undo's.

Suppose before every change you save the old values. To undo, restore the old saved ones. That's huge overkill, but it's simple and it works. Memento says to make the saved variables private, and create a save() and restore() function.

 

Summary

Let's narrow down Design Patterns to what they are. They're not algorithms - how to do things - and they're not simply advanced programming. Let's just look at them as what they are - ways to organize larger programs. They're old, but that's not necessarily bad. But in this case they use plenty of terms requiring a tortuous explanation of how they made sense back then. They focus on a pure OOP style which isn't as helpful as we'd hoped it would be. They're a common set of terms used by programmers - except almost none of them are, and not in the same way.

Having said all that, it seems it can hardly hurt to look over just 23 tricks that seems to have caught on. Do that. Skim the definitions on the internet, go back and read the examples and so on. Take an hour or two. At the very least it's a chapter about programming history, which is always fun. At that point, you get to decide whether to commit to weeks of study. Don't. There are much, much better ways of learning the same things.

Facade and Adaptor are two examples of wrappers. Better to explain it that way ("here a wrapper makes a class easier to work with, here we adjusted it to fit a 3rd-party interface). In the book, Facade and Adaptor aren't even next to each other. Likewise, SOLID, Factory, Dependency Injection, MVC, and Service Locator are the same "use an interface to loosely connect parts" concept. Educationally, we're hiding that simple idea by breaking it into unconnected parts.

Meanwhile, sprinkled between are State, Visitor and Decorator, which should be skipped (or at least put into their own extreme OOP section). Likewise Observer and Flyweight aren't organizing tricks. They should be grouped with all of the other non-Design-Pattern speed tricks. Compound - skip it and learn recursion. Flyweight - read intro to relational databases (Com Sci students take a class in it, for this reason).

My inspirational plea to emotion: Design Patterns don't make any sense at first, but that's good, right? It means they're deep, and it will be like a secret handshake once you know them. But you can say that about anything that doesn't make sense.

 

 

Comments. or email adminATtaxesforcatses.com