What stays the same? What changes?
The patterns are the same. The language techniques change.
Are there guiding principles like SOLID,
Yes. Indeed, they remain the guiding principles. Nothing changes.
or canonical patterns (perhaps entirely new ones) that a dynamic language newbie should know?
Some things are unique. Mostly the impact is that the implementation techniques change.
A pattern is -- well -- a pattern. Not a law. Not a subroutine. Not a macro. It's just a good idea that gets repeated because it's a good idea.
Good ideas don't go out of style or change dramatically.
Other notes. Python is not "weakly typed". It's more strongly-typed than Java or C++ because there's no cast operation. [Yes, there is a way to fudge the class associated with an object, but it's not the kind of thing that's done except to prove a fussy, legalistic point.]
Also. Most design patterns are based on different ways to exploit polymorphism.
Look at State or Command or Memento as examples. They have class hierarchies to create a polymorphic states, commands or mementos of state changes. Nothing changes significantly when you do this in Python. Minor changes include the relaxation of the precise class hierarchy because polymorphism in Python depends on common methods not common ancestors.
Also, some patterns are simply an attempt to achieve late binding. Most Factory-related patterns are an attempt to allow easy change to a class hierarchy without recompiling every C++ module in the application. This isn't as interesting optimization in a dynamic language. However, a Factory as a way to conceal implementation details still has huge value.
Some patterns are an attempt to drive the compiler and linker. Singleton, for example, exists to create confusing globals but at least encapsulate them. Python singleton classes aren't a pleasant prospect. But Python modules already are singletons, so many of us just use a module and avoid trying to mess with a Singleton class.
If I understand your question correctly, the answer boils down to this:
TLDR
The reason for having things like abstract classes and interfaces/protocols in a dynamic language like Objective-C are mainly simplicity and cleanliness of code.
The longer version
As cool as duck-typing is, it's actually a huge hassle if you cannot make any assumptions on the type of an object:
every respondsToSelector:
line in your code messes it up. It adds noise that obfuscates what you are actually trying to accomplish for little to no benefit.
Besides, it's shit-work.
So if you provide an API contract by loosing some dynamism, using a more statically typed model it's almost always a win:
- Replacing multiple
respondsToSelector:
methods with a single conformsToProtocol:
is a win.
- Having a succinct and clear listing of the necessary methods in the protocol definition is a win. (Writing that definition in and of itself is shit-work as well, but it keeps you from doing more shit-work in a number of places, so that's tolerable.)
- Based on that, having the compiler warn you if you omitted implementing any one of those methods is a huge win.
- And: Not having to write a single
respondsToSelector:
or conformsToProtocol:
message at all, because the compiler can now warn and tell you where you messed up, is the biggest win of all.
If you can go further than that by providing a common foundation of actual implementation, creating an abstract class might be an even bigger win (probably not so useful in the context of delegation...) — after all, repeating yourself in implementation is the worst case of shit-work, as every change will result in more shit-work to keep things consistent.
So yes, dynamism is a cool and useful thing to have, as it allows you to pull off all kinds of fun tricks. But in practice, getting rid of some of that (where appropriate) can make your life much easier and keep you from doing shit-work — like writing respondsToSelector:
.
Best Answer
First thing to understand is that design patterns are not a design tool. They're a communication tool. Can you write a good application without knowing the GOF design patterns? Absolutely. You'll probably find you use design patterns already, you just don't know it. After all, they are common solutions to common problems. But can you tell a colleague how to do something better? Not easily.
Second thing to understand is that some design patterns are so common that they're now built into most modern languages. For example, you don't need an Observer pattern in C#, you have events (which are the Observer pattern, but you never need to say to a C# developer "use an Observer there").
Third thing is that the GoF design patterns book was written
several versions of Javamany moons ago and was based very much on class-oriented languages. Javascript isn't a class-oriented language, it's a prototype-oriented language, and Python and Ruby are both dynamic. So some of the GoF patterns may be irrelevant to those languages (but some may not).Much more useful nowadays are the patterns described in Martin Fowler's Patterns of Enterprise Application Architecture.
All this said, it's useful to understand the GoF patterns, if only to facilitate communication with other developers who understand them. The GoF book is dry and hard to get your head around sometimes -- they also make for an interesting thought exercise, they might prompt you to see common patterns in your team's application and write your own patterns. It's also not very precise (when you talk about the difference between Adapter, Proxy and Facade, for example).
There are some good sites out there with much clearer descriptions.