Design Patterns

Started by
17 comments, last by PKLoki 16 years, 11 months ago
OK, I've just finished writing a 10 page paper on design patterns (specifically, the Gang of Four patterns) and I'm left wondering ... how relevant are they? Keep in mind, I've not yet gotten my hands on the GoF book. However, as my professor thrusts these ideas down my throat, I realized that I've gotten by just fine without many of the patterns presented. Which patterns are most relevant to game design? Which patterns do you like/dislike? Any thoughts from the pulpit?
~Argonaut________________________________Why "~Argonaut"? It's all just a mathematical expression denoting a close approximation of "Argonaut", which is irrational and can't be precisely defined.
Advertisement
Using this site as a reference: http://www.tml.tkk.fi/~pnr/GoF-models/html/

Behavioral patterns
-------------------

Command: used heavily in threading/scheduling. Java's Runnable interface is a prime example.

Iterator: Java's enumerator, C++ stdlib iterators, etc. It's also a good idea to build your own iterators sometimes, to traverse non-linear structures (e.g. permutations of numbers).

Mediator: essential for complying with the rule of demeter ("delegating").

Observer: this is so often used it should require no further elaboration.

Strategy: used in modular algorithms (an example from AI: separating the search algorithm from search strategy).

Template method: an alternative to the Strategy pattern, used e.g. in Java's ResourceBundle (most of the implementation is given, you just need to plug in some of your own).

Visitor: a rather complex pattern IMHO, but allows doing type-safe multiple dispatch.
Creational patterns
-------------------

Abstract factory / Factory method: these are often a useful and preferred way of constructing objects, since they allow making the newly constructed objects "context sensitive". For example, if you let your Company object create Employee objects, the Company can automatically set some of the the Employee's fields to comply with the Company's policies. Java's XML frameworks use these patterns.

Singleton: ah, the infamous Singleton... IMHO it's used too widely ;) Basically, using global objects destroys modularity, since clients of the Singleton must agree about its state - or in other words, each client must be kind of "aware" of each client. Used quite a bit in Java: for example, class XXX { public XXX getXXX(); } or class YYY { public YYY getInstance(); } speaks of the Singleton pattern.

[Edited by - uttumuttu on May 20, 2007 5:51:42 AM]
I'm a noob when it comes to design patterns, but once I tried out the Decorator pattern, I was very pleased with it. I'm looking forward to using more of the patterns when the need arises.
Structural patterns
-------------------

Adapter: used when you want to used legacy software to a new framework. Using an adapter is better than rewriting, since it respects the open/closed principle.

Composite: useful in modular frameworks. For example, if some class supports only a single Observer, you can still write a BroadcastObserver class that broadcasts the observations to multiple Observers.

Decorator: my favorite pattern! Used e.g. in Java's stream framework, where you can make any kind of InputStream buffered by creating a BufferedInputStream that uses the original InputStream as its source.

Flyweight: caches can often be described as Flyweight factorys.

Proxy: essential for complying with the rule of demeter.

[Edited by - uttumuttu on May 20, 2007 5:04:45 AM]
Some afternotes
---------------

Clearly, design patterns are as relevant as ever. The motivation for design patterns can be traced back to a few simple "meta principles":

* Automatic dispatch: this is the pinnacle of all object oriented languages. Instead of having huge switch clauses, frameworks based on sound patterns implement dispatch automatically.

* Static over dynamic (explicit over implicit): instead of using the Command pattern, we could have simply used a function pointer (in C++, at least). But using the Command pattern makes our program more explicit. Named abstractions are a powerful, self-documenting tool.

* Open/closed principle: this principle says, essentially, that you should never "modify" old classes, but only add new classes that help old classes interwork in new ways. For example, the Adapter pattern is a great example.

* Loosened coupling: by interworking through abstractions, programs can eliminate cyclic dependencies (see John Lakos' "Large Scale C++" for elaboration).

* Rule of demeter: essentially, objects should avoid "train code", such as engine->getObjectList()->getObjectAt(0)->setVisible(true); and replace it with code such as engine->setForegroundVisible(); This is really just a special case of loosing coupling. "Pragmatic Programmer" includes some further (non-academic) elaboration.

(Ah, isn't doing other people's homework the best way to avoid one's own? ;)

Take care,
- Mikko

[Edited by - uttumuttu on May 20, 2007 5:12:56 AM]
I've found design patterns to be most helpful in *naming* a process/technique/concept that you want to implement. If I know what it's called finding standard/optimised/generic implementations in any particular language is going to be reasonably easy.

It's also a great communication tool - rather than having to draw annotated UML for a pattern you can just direct other developers to a particular pattern and the reference in GoF.

Sometimes it's also nice to be able to look at someone else's code and be able to identify 'code smells' (ala Martin Fowler 'Refactoring'), and use your knowledge of patterns to help provide direction for extracting pieces of code back to easily identifiable patterns.

Patterns are definitely something you should know about, they'll make you a better developer, and more useful in a team situation. But also be aware that design patterns aren't a silver bullet. One example is that sometimes features of a particular language can restrict your designs to a subset of patterns that are easy to implement. If you need to move languages, be prepared to accept that new paradigms/techniques and patterns may be more or less suitable than you are used to.
Quote:Original post by argonaut
However, as my professor thrusts these ideas down my throat, I realized that I've gotten by just fine without many of the patterns presented. Which patterns are most relevant to game design? Which patterns do you like/dislike?

If you're getting on fine without patterns, chances are you're:
1. Writing very small programs.
2. Writing bad code.
3. Using some less formal variant of patterns already without realising it.

Or any combination of the above.

IMHO if you start thinking "where can I put pattern X in my code" you'll get overengineered and generally bad results. But if you know the patterns then you can spot when they're naturally emerging from your code and formalise/improve the implementation.
When I first read about design patterns, I couldn't make much sense of them. But the more programs I write, the more I find places where I can apply them. Perhaps some people can read the GOF book and understand it instantly, but I think one needs to have experienced at least some of the problems the patterns are meant to solve to appreciate them.
Design patterns are just that: patterns. They're derived from empirical observations of the high-level data and control structures used in successful programs; they're not a set of prefabricated immutable components to be dogmatically imposed upon a system's design. In this context, "successful programs" refers to productively-developed, correct, reusable and maintainable programs.

The merit of design patterns is that they work. In general, if you're using a programming language with single-dynamic-dispatch object-orientation (e.g. C++, Java, C#) and you need your program to perform differently depending upon the types of a pair of objects, then a scheme of two-stage delegated dispatch - i.e. Visitor - has proven to be an effective implementation of that.

Design patterns are also useful because they can save you extra work in the long run. If you're using half of a pattern now, later on you may actually find you wanted the whole pattern.

Suppose you make an object with a PushState and PopState methods. That's half of the Memento pattern, and has the disadvantage that you can only manage the state on a FIFO stack, whilst the full Memento has (e.g.) RememberState and RestoreState methods which allow you to separate the mechanisms of state-management from the policy.

If you're aware of Memento, then writing a PopState method should make your thumbs prick: you'll think about whether it could be better to use the full Memento, knowing that enough other people have found Memento useful enough that it's been identified as a recurring pattern.

Design patterns will always be relevant, although if you're really good you shouldn't have to think about them: they'll happen instinctively.

This topic is closed to new replies.

Advertisement