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, it tells you about "Design Patterns". Boy does it tell you. The explanations don't make any sense, but they say that just means you have to keep trying.

This is an explanation of how Design Patterns fit into computer science, what you should be learning instead, how such silly things became so popular, and at the end, an explanation of each Design Pattern so you can judge for yourself.

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.)

As far as getting better at programming, here's a short list of useful things to learn before Design Patterns. A 2nd semester programming textbook should have them: using loops to do common things with arrays. Lists - nicer versions of arrays. Using linked-lists to insert anywhere, or hash-maps 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 inheritance, virtual functions and the Animal a=new Cat(); trick. Using Big-O to analyze speed.

Design Patterns are meant for programmers who know most of that stuff. Especially they assume you're good with base classes, inheritance and virtual functions.

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 1994 book about the "new" OOP style.

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:

There's a well-known 1975 software engineering essay by Fred Brooks, an old-time IBM'er, "The Mythical Man-Month". He describes many of these problems, and fxes, but mostly says that writing a large program is unpredictable. We can try to account for that, but we can't remove the unpredictabilty. He wrote a follow-up 10 years later, "No Silver Bullet", saying he was more sure of that than ever. We've come a long way with better computer languages and such, but that was the low-hanging fruit. There's not going to be any miracle new way of writing programs.

 

Object Oriented Programming

Way, way back we figured it was a good idea to make groups of data that counted as one thing, like name and address. It should also have custom ways of playing with itself, like "print as if it were a mailing label" or "is in the same city". Not much after that we realized it was better to always use these -- never just reach-in and change some data; there should always be a pre-written function doing what you want.

Object oriented formalized that. You officially list a group of data along with the stuff other people can do with it, making it illegal to bypass and play with it directly.

At the same time we developed rules for making sub-types of thses groups of data. You can tell the computer that Sheep, Cows and Pigs are all types of Animals. You can ask it to do something to a certain Animal and the computer will automatically figure out which type it is and do ti the right way. In OOP terms that's inheritance and polymorphism and dynamic dispatch.

Here's where it gets interesting. 20 years earlier we invented something called Structural Programming. We'd been adding more and more commands to make programming easier, but then realized we could also ban commands. We did that, and forced the program to have a certain structure, and programming was much easier.

People thought maybe we could do the same trick with OOP. What if we forced everyone to use classes and polymorphism for everything? We could put them into a UML diagram, have a program analyze it, we were very excited.

This is where the Design Pattern book came in. It was about ways to rewrite things to be more OOP. It was really just a simple survey of OOP tricks people around the world were using, and what names they were calling them. They made up the phrase Design Pattern since it sounded cool. It was later on that people took it as the new bible for OOP.

But OOP-all-the-time didn't work out. Pure OOP programs weren't any better. They were actually slightly worse. Today were just use whatever tricks seem best. The OOP tricks are useful in many places, and not so great in others.

Things to note about Design Patterns

 

Explanation of each Design Pattern

Officially, the things in that one book are the real design patterns. But there are a few things the book describes and says aren't design patterns, but now people say they are. I listed those, too.

When the book was written, Java and C# didn't exist. The book examples tend to be in C++ or SmallTalk (an experimental OOP-like language like nothing you've ever seen). So i'm not going to try to explain the book examples.

Some of the descriptions in the book often use old, experimental terms, or just odd phrasing that sounded fine to a 1990's software engineer, but is awkward now. It wold be way too mcuh work to try to translate them.

This might be something

For one design pattern the name really caught on, people mostly use it the right way, and it's a real trick.

Factory/Abstract Factory

A nicely modular program has it so you can change one part and won't need to redo many others. Inheritance handles that wtih base classes; Animal a1; can point to any animal without knowing about it ahead of time. Another section would hook up a1=new Pig() and it all works.

But what if the first part needs to make Animals? It can't make Pigs unless we add that =new Pig(); somewhere. Arg!

The Builder pattern says we can keep it modular by having a plug-in a creation function. It looks like this:

// Section one. Will never be changed for new animais:
Animal a1;

// someone else wull assign to this:
AnimalMaker maker1;

// this is how we create animals:
a1 = maker1.getAnimal();

Before we start, someone else can set maker1={ return new Pig(); }. The line a1=maker1.getAnimal(); can make Pigs all day even though it never heard of them.

It's not profound, but it's a neat trick sometimes.

They are listed as two different Design Patterns. Technically Factory makes just the animal, while Abstract Factory makes a few related things, like the animal and its food. But people call everything like this a "Factory".

New words for old ideas

Several design patterns are just older ideas rewritten to feel more Object Oriented. That seems pointless, but remember the book was about what some people were actually doing.

Composite

This is another name for a tree, which is a standard computer science thing. We start by teaching recursive functions, then go on to trees (which are recursive data structures), then types of trees and tree functions. Trying to learn the "Composite Design Pattern" is backwards.

So why call it Composite at all? I have a theory. Tress can be divided in speed and natural. Speed-up trees are much more common -- some list is scretely represented as a tree. More rare is when we naturally want to think of data as a tree. A file system is an example.

My theory is that OOP people are all about hiding data, and most trees are hidden speed-up trees, so most OOP people don't like trees. Composite is a way to say "natural trees are fine".

Interpreter

This is making a mini-language in your program. For example a shopkeeper in your game says: "$charName$, welcome to $cityName$" which is filled in with the correct name and city. A little more complex: the AI for a patrolling guard is "W[3,4]P(8)W[0]", which your program knows is "walk to station 3 or 4, pause for 8 seconds, then walk back to station 0".

It's a good trick, from before the book came out. It seems complicated but it's better than the alternative. It's also not especially object-oriented which is probably why no-one talks about it as Design Pattern.

Prototype

This says you can create a complicated object by merely copying one you already have. That's a good idea but with a huge catch -- how did you get the original? Well, you might be in some sort of framework that creates a few user-designed objects behind the scenes. If not, there's no way to use this trick.

This isn't especially Object Oriented and no one really talks about it much. People do it when they can, they just don't think about the Prototype Design Pattern.

Adaptor, Proxy, Facade

We often have to use some class we can't change whose commands we hate. We can still change the commands by adding a "wrapper" class in front. Suppose we have an overly-complicated file system class. A typical wrapper might give us an easy "open file" command:

class NiceFileSystem {
  // the real file system class, which we hate:
  fileSystem fs;

  // our verion of the command to open a file
  fileStream openForReading(string name) {
    int complexThingNeededForRealClass = .....
    x=fs.partOneOfComandWeHate(name, complexThing);
    return fs.partTwoOfCommandWeHate(x, math, moreMath);
  }

We just turned that ugly mess of commands into a nice, simple openForReading("cows.txt") command, using a wrapper. Notice how the real class is hidden inside the one we add (that's why we call our new one a wrapper -- it wraps around). We usually finish up by writing a constructor to set fs.

Adaptor, Facade and Proxie are officially 3 Design Patterns but they all work this way. Facade is if you just want nicer names. Adaptor is if you have to change the names to make it fit. Proxie is the old word for when someone else makes you use their wrapper even though you might prefer using the real one.

Things that didn't have words

There are a few design patterns which are simple tricks that didn't really need names.

Singleton

For various reasons we sometimes like putting our global variables in a class. Nothiing special about it. Instead of BRD_WIDTH we'd use levelData.BRD_WIDTH. The only complication is that someone needs to create an instance of the class and then make sure no one else ever overwrites it with a new instance.

Somehow that became "a class with a single copy" which became Singleton.

But we use the trick for non-single things. A game could have a set of global data for each game level. The real global levelData would point to the current one. We're using the Singleton trick to easily swap in new global data. Maybe we should call that a multi-ton.

MVC

Breaking thngs into parts is always good. We often want a part that only manages our data. The visual part is almost always seperate -- data that knows how to display itself sounds cool, until we want it in columns, or a pie chart, or by state ... . After data and display we need some code to work with it, which gives 3 major parts in a simple data-manipulation program. That's MVC. It stands for Model, View, and Controller (model is the computer word for data).

You'll often see frameworks calling themselves MVC. That roughly means they support mixing-and-matching any database with any way to show it. Microsoft's asp.net MVC web-page designer probably lets you use microsoft's database or Oracle's.

A funny thing, the Design Pattern book specifically says MVC is not a Design Pattern, but people call it one anyway.

Bridge

One of the main tricks in OOP is using a base class as a plug-in. engine eng; can be assigned any type of engine. "Bridge" says to use this trick to make parts of complex objects. For example, an airplane which has several possible engines and seat arrangements:

class Airplane {
  GenericEngine engine; // point to any engine subclass
  GenericSeating seats; // points to any seat subclass

If there are five types of each, we can use this one class to make 25 types of airplanes. Here's the crazy part: OOP people actually wrote 25 airplane classes, using inheritance (26 if you count the abstract base airplane class). OOP rules said you had to write a TurboProp subclass with 5 sub-sub-classes for seats, and so on. Bridge pretty much said that was crazy, and crazy isn't good OOP; do it the normal way.

One of the most famous lines in the book is "favor composition (combining things) over inheritance". This is what it means.

If you read the Bridge patttern it seems to say something completely different. Most people quote that and are done. But "use oomposition" is the trick if you read it all the way through.

Dependency Injection

This says that when a class has sub-parts, they can be defined outside of it. The class doesn't even need to know how to add them. It can rely on someone else to finish making itself. For example:

class Airplane {
  Engine eng; // pointers to base classes
  Seats sts;

  // when you create an airplane you're required to supply
  // a created engine and seats:
  public Airplane(Engine ee, Seats ss) {
    eng=ee; sts=ss;
  }

The person making the airplane would do all of the work:

Engine e1=new TurboProp(); // a subclass of engine
Seats s1=new econoCouch();

Airplane a1=new Airplane(e1, s1);

The alternative sounds nice. If the airplane knows about engines and seats we could simple request a "DC-10" and let the airplane find the right parts. But that can be messy -- the airplane class is stuck with all of these engines and seats and airplane rules no one program ever needs.

The icky name "Dependengy Injection" is supposed to mean that Airplane has no extra Dependencys. It only needs to know about the base classes Engine and Seats. You "inject" (eeww) the specific engine it needs.

Many GUI-based editor programs secretely work like this. The panel for Airplanes has a drop-down to choose engines and seats. When your program runs, it magically creats them and assigns them in your plane. If you search your part of the code for where an Airplane gets its seats, it's nowhere. Those are sometimes called "DI frameworks".

"DI" is not in the book. It was made-up later. The definition is also squiggly. Some say the constructor must take every input. Others say you can plug them in however you want and it still counts.

Builder

This means to build complicated things in 2 stages. Suppose your program needs to be able to make any sort of tetris shape. You need to make them on-the-fly, and want them to have elegant right-angles and T's and straight sections. Stage 1 can be filling out a grid, then stage 2 analyses the grid and creates the piece. It's nice since users will just fill out the grid and tell stage-2 "here, make this".

It's an old trick, a good one, and not especially object oriented.

Flyweight

This says to factor your data like in a relational database. That's it. Computer science undergrads take one database class so that they understand this (but they don't call it FlyWeight, they call it "organizing your data the normal way").

If you've never used one, suppose you have a list of [name, age, country, national bird]. Clearly that's nuts. It repeats the same national bird. You'd store it in two tables like [name, age, country code] and [country code, country name, national bird]. That's factoring data.

A common way to mess this up is mixing variable data with constant type data. Suppose you make a killer robot with [name, model, current health, max health, weaponType]. You can split that into the stats for this robot and the stats for each model: [name, model#, current health] and [model#, model name, max health, weaponType].

Mediator

This is another word for a manager or controller or just "middleman". If several classes work with each other and you want to be able to switch out some, make a class that coordinates them all.

Command (v. I)

This is when you create an object holding a command. Instead of calling menu1.button1.hide() you could create the command [object: button1, action:hide] and send it to menu1.

One advantage is looser coupling: we only need to know how to make commands, not the exact functions to call. That can make it easier to switch parts around. The other advantage is being able to save commands, maybe for playback, or record-keeping in a financial system. The obvious drawback is being more complicated.

Obsolete

The book is almost 30 years old. Some things in it are obsolete.

Iterator

An iterator is a interface giving a generic way of going through a list. It can be used to make a list-processing loop which works over any type of sequence. You may be thinking "wait, don't foreach's do that?". Yes, they use iterators. Iterators are such a nice idea that we built them into most languages 20 years ago.

So iterator as a Pattern is obsolete in the sense that you don't need to study how and when to use it. You've been using it just fine this whole time.

Inversion of Control

This is a crazy dinosaur of a term. It means using buttons or other GUI controls that can "talk" back to the program. Javascript's OnChange event or button1.onClick+=myFunction are "IoC". You'll sometimes hear people call visual editors which let you drag in button "IoC frameworks". No reason except it sounds kind of cool.

An obvious question is how anyone decided to call buttons "inversions". Well, back in 1980 the main program handled all input. It asked you to enter your name and waited, then age, and so on. That was how all programs worked. When were starting to invent things like web forms, a button telling the program that it was pushed seemed backwards -- an Inversion of Control, get it?

The original book doesn't call this a design pattern but it used the term. Later, people started calling it a Pattern.

What is this?

The original paper with 23 design pattersn was just a survey. Just a list of things people were doing. Some of them were clunkers.

Observer

Normally when we change data we immediately redraw the display through a listener. That's an easy modern approach. But what about things that change 20 times all at once? It might be better to wait to redraw until they're all done. That's the Observer pattern.

But wait -- what does it say to do? Well, nothing. This pattern is only some suggestions. Maybe speed isn't a problem so we do nothing. Or we could have the user manually call for a redraw. Or use a flag.

Service Locator

Globals such as files and databases are sometimes called Services, just because. Since systems vary and can change, it makes sense to provide a function to find these things. That's a Service Locator. It's not in the book, but people say it's a pattern.

Memento

This is a very specific thing about making a system for undoing changes.

Whenever you make a change, save the old values. To undo, restore them. Memento is one more step: make the saved variables private, and create save() and restore() functions.

Chain of Responsibility

This is when you have a function might not be able to handle some inputs but has a plug-in as a fall-back, and that has another, and so on. It's vague.

A function which always tries X, Y then Z is just a normal function. This is more when your system can tell the function "if you get this error running on me, go here and try this".

Being more OOP

As mentioned, the official goal of Design Pattern was to have more Object-Oriented ways of doing things. Many are things we could do just fine before, but hopefully doing them more OOP-style would pay off later.

Template

This one is classic inheritance theory. We write a class with plug-in parts which customize how it does something. The exact method: we override them in a subclass.

For example this class does something special to words checkName likes:

abstract class doStuffToCertainWords {

  // sub-classes override this to change which words to use:
  abstract bool checkName(string w);

  // the main function, which uses the plug-in:  
  void processWord(string curWord) {
    if(checkName(curWord) { ... }
    ...
  }

To make it use words of only length 5, we'd make a subclass and override checkName() for length 5:

class doStuffToL5 : doStuffToCertainWords { // subclass
  override bool checkName(string w) { return w.length==5; }
}

The base class would decide which parts need to be customizable and turn them all into overridable functions.

Command, v. II

The Command Design Pattern has a second, completely different meaning: plugging in a function using a class. The secret to understanding it is that plugging in just a function is easier. Command-II is for when we really want a class and don't mind that it's more work.

To make the spot where we plug things in, we'll obvious use a base class. It will have one function which sub-classes willoverride. In use:

// virtual base class:
abstract class ButtonAction {
  public virtual void clickAction() {}
}

Our buttons will hold one of those and call clickAction as needed:

  // inside Buttons:

  public ButtonAction myAction; // plug in your "function"
  
  void handleClick() {
  if(myAction!=null)
    myAction.clickAction(); // call the user's function

This next part is new and weird. For each function you want to fgive, you need to create a new subclass holding it. This tells the button to print "cow" on each click:

class CowSayer : ButtonAction {
  public override void clickAction() {
    print "cow";
  }
}

To plug it in we need one more step -- we need to create an instance. We don't need especially need one, but the trick won't work otherwise:

  button1.myAction = new CowSayer();

When the button is clicked it will run myAction.clickAction();, which we overload, and "cow".

In theory this trick is nicer when the class has several functions we want to plug in all at once. But it's still a pain for even that. Java can't pass functins -- ot can only use the Commnand-II Pattern trick -- but they added a shortcut where you pass a function and it does that work. Even Java thinks Command-II is annoying.

Strategy

This does the same thing as Template -- a class with plug-in customization for how to do things. But instead of inheritance we plug them in using the Command-II pattern. The old word-managing class would look like this:

class doStuffToCertainWords {
  // can assign a function here that checks words:
  StringToBool checkName;
  
  // using it:
  if(checkName.useWord(curWord) { ... }

As before we have to define the abstract StringToBool class with one overridable function, create a subclass, and so on. We use this general pattern today, except we plug-in just regular functions.

Decorator

We're starting to get into the truely complex ones. Decorator is about making dynamic (as the program runs) changes to how a class works. We may change a Dog to eat slowly, then later change it back to normal.

We could do this with plug-in functions, but Decorator is about using wrappers. As we make dynamic changes, we'll add more and more wrappers.

Let's start. For technical reasons, we'll need a main interface. Everyone (including the normal Dog) will use it:

abstract class Dog {
  virtual void eat();
  ...
}

class BasicDog : Dog {
  // dog variables go here

  // the normal dog eat function:
  override void eat() { ... }
  ...

Our program will start with Basicdogs: Dog d1=new BasicDog();.

Each possible change requires a new wrapper class, making only that one change. This would change the eat() function to go slower:

class EatSlowDog : Dog { // <- inherits from  Dog
  // holds the original Dog, or a wrapped Dog:
  Dog d;
   
  void eat() {
    // new slow-eat code here
    // possibly uses d.eat()
  }
  
  // standard wrapper constructor:
  EatSlowDog(Dog oldDog) { d=oldDog; }
}

We coul dmake our dog eat slowly with d1=new EatSlowDog(d1);. The old d1 is still there, buried inside the new slow-eating one.

Here's where it gets weirder. To eat at normal speed we need another class, maybe EatNormalDog. We can't take off a wrapper, only put pon a new one:

// eat back to normal:
// pretend we wrote an EatNormalDog wrapper:
d1=new EatNormalDg(d1);

It gets worse. Every wrapper needs pass-through's for every function it didn't change. Suppose we made 10 changes, including things we changed then changed back. That's 10 wrappers in a row. When we call d1.sit() the 1st wrapper might go to the 2nd, to the 3ed, and that one changed sit and does it's thing. If we never changed how the Dog barks then d1.bark() gets sent through all 10 until the orignal Dog barks the normal way.

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. Here we have a 4-stage jumping process:

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

The code uses the state js to decide what to do. When we enter a new state, we change js to remember:

if(js==waiting) { if(spaceKey) js=launching; force=0; }
else if(js==launching) { 
  if(abortKey) js=waiting;
  else { force+=t*0.4; if(force>LIFT_OFF) js=soaring; }
}
else if(js==soaring) { // and so on
...

The State Design Pattern does the same thing, but using classes. First we make an abstract State class with a doAction virtual function. Each real state makes a sub-class:

// abstract main class:
abstract class JumpState { virtual void doAction(); }

// a subclass where the function waits to jump:
class JumpWait : JumpState { 
  void doAction() {
    // copy of the waiting code from before:
    if(spaceKey) { backpointer.js=new JumpLaunch(); force=0; }
  }
}

The old js holding a state will now point to a state subclass, calling doAction():

JumpState js; // can point to any "state" subclass

js = new JumpWait(); // we start by wiating

  // main loop:
  js.doAction();

The secret is that each subclass will reach up and change js as needed. At first js.DoAction() runs the wait code. When they jump js points to a JumpLaunch subclass and js.doAcation() runs the launch code.

The core of the State Design Pattern is abusing an important feature of inheritance: dynamic dispatch. Whenever you call a.doStuff() on a class you get a free IF/ELSE. The system says "if subclass A, do this, if sunclass B, do this ...". In theory, you can get rid of any IF/ELSE by converting the options into subclasses.

To sum up, the idea of a State Machine is pretty useful. But there are easier ways than the State Design Pattern.

Visitor

This is the last one, and the most complicated of all. The problem is solves will take a while to explain. Suppose you have a list of Animals, of all types, and want to run A[i].proce() to get the total cost of the whole zoo. Except there's mo virtual price() function and you can't add one. How can you do it?

The obvious hammer-and-tongs method is hand-checking each type. This is not the Visitor pattern:

foreach(Animal a in Zoo) {
  Cat c = a as Cat; // is it a Cat?
  if(c!=null) { price+=catCost(c); continue; }
  Dog d = a as Dog; // is it a Dog?
  if(d!=null) { price+=dogCost(d); continue; }
  ... 
}

As another warm-up let's try using overloaded functions. It won't work, but let's see why:

// 2 different functions, for Cats/Dogs, overloaded with same name:
void priceOf(Dog d) { ... }
void priceOf(Cat c) { ... }

Animal a1 = new Dog(); // testing:
n=priceOf(a1); // ERROR

Overloads can't do dynamic dispatch! In English, priceOf(a1) is too stupid to know a1 is really a Dog. All it can ever do is try to run the priceOf(Animal) overload (which we didn't write and would be useless if we did).

Now we can start the Visitor pattern. Step one: the original class needs to be set up to use it. That's right -- if you have a normal class, you can't use Visitor. They need to have written the class to allow it. The class needs an extra plug-in class which users can subclass for their "function bundle":

// base class with generic slots for each Animal
class AnimalVisitor {
  virtual void catVisit(Cat cc);
  virtual void dogVisit(Dog dd);
  ... every other animal
}

Use it the normal way: write your real functions in a subclass (this is the same idea as version-II of the Command pattern):

// real functions, written as a subclass:
class AnimalCost : AnimalVisitor {
  override void catVisit(Cat c) { globalCost+= ... }
  override void dogVisit(Dog d) { globalCost+= ... }
  ... every other animal
}

Step 2 of preparation is having the original class use these things. It gets a virtual function receiveVisit. Nothing special, yet:

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

But now each each subclass overrides it. Remember, the input is the bundle of functions for every animal. Here a Cat finds the cat function:

class Cat : Animal {
  ..
  void receiveVisit(AnimalVisitor av) { av.catVisit(this); }
  //                                       ^^^ cat
}

class Dog : Animal {
  ..
  int receiveVisit(AnimalVisitor av) { av.dogVisit(this); }
  //                                       ^^^ dog
}

That's the whole thing. Here it is getting the correct price for each Animal:

// create a Visitor with our pet-feeding functions inside:
AnimalVisitor coster = new AnimalFeeder();

foreach(Animal a in Zoo)
  a.receiveVisit(coster);

Let's walk through. Assume A[i] is a Cat. A[0].receiveVisit(coster) runs the Cat version of receiveVisit. That runs coster.catVisit(A[i]). That runs our catVisit which gets the cost of the Cat.

The whole pattern is figuring out a way to trick the class into looking up the ocrrect subclass for us, which receiveVisit does.

This pattern really shows the intent of Design Patterns. Visitor is too messy to want in a normal program. You need to be writing a class that people you never met will be using, whom youy assume know the Visitor Pattern and want to use it.

 

Summary

So what do we have? We can skip the "very OOP" ones, but if you see a class with a receiveVisit function you could use that without too much trouble.

Many use the basic OOP trick where Animal a1; points to any subclass. That's a good trick. Many are for loose-coupling (DI, Factory, MVC, Mediator and Command v.I). Loose coupling -- having parts connected to each other in fewer ways -- can be useful in larger programs. The idea of the Strategy pattern is good, except we plug in functions today instead of classes. In general knowing how to treat functions as variables, "first class objects", is useful. Also, trees and recursion.

You could learn some of that by studying Design Patterns, but it's easier to learn it directly. Design Patterns aren't wrong so much as they're just not a useful way of thinking about programming today.

 

 

Comments. or email adminATtaxesforcatses.com