“Design Patterns” Aren't (2002)
perl.plover.com
perl.plover.com
foreach only works on a list. I can bless a list of lists into a rose tree, but i can't traverse that in a uniform way. There's just no hook to say, when the thing isn't a list, get the next element like this. Now days i'd point at haskell's traversable as the gold standard.
On large java projects it seems like composite/facade for days. Those splits seemed to let teams work together and not end up with piles of rubble.
A lot of my understanding of programmer fashion back then was pretty fuzzy. It was easy to ask any team if they were doing extreme programming and they'd all respond, well, kinda. Ask any team if they were using a pattern language, and they'd mumble about UML. Sort of the equivalent of well, kinda.
When you have to write a large project like a compiler or a web browser or something, it's handy to have around. All to often I stare at the empty text window in my ide and question my life choices. I wonder why i didn't learn to weld and why i'm a failure at everything i try. The existential dread fills my soul and i want to just crawl under my desk and cry. In that case, i'll flip through the GoF. Then i tell that endless abyss "I'm going to use the Command Pattern!" The abyss slinks back into the corner quietly murmuring until i feed it a couple beers at the end of the day.
On the other hand, i did quite a bit of perl programming around 2000, so i'm probably weird.
It appears to me that almost everyone who has read this has completely missed the point, even though I said it twice in big letters. People keep sending me mail that says things like this:
> I just read through you article "Design Patterns" Aren't. I'm afraid you got it all backwards. At least the iterator example is totally flawed.
Or people spend a lot of time arguing about how foreach is not analogous to an iterator pattern, or how it doesn't do the same thing, or how even if it does, I'm still wrong, because Perl has no analogous replacement for a model-view-controller pattern, blah blah blah. None of this has anything to do what the point of my talk. I really wish I'd left out the thing about the iterator pattern.
Why do people focus on pages 4, 5, and 6 of a 13-page talk, and forget all about slides 8, 9, 10, 11, 12, and 13, where I make the real point? I have a few theories. One theory is that most of these people are technical experts. They're equipped to consider technical issues, but not much else. When all you have is a hammer, all you can see are the nails. That's what happened to the guy above who said I "got it all backwards." What? The point of my talk was that we need to take a fresh look at Chrsitopher Alexander; if I got it backwards does that mean he thinks we should avoid taking a fresh look at Christopher Alexander? Or does he think that Alexander should be looking at us instead? No, he just forgot about everything after slide 7.
Why do people focus on paragraph 2, when paragraph 5 is where i make the real point? Facing a big coding challenge is hard, and having an abstract language for describing how to structure a solution is valuable to the point of protecting sanity. I have a few theories. One theory is that most of these people are technical experts. They're equipped to consider technical issues, but not much else. When all you have is a hammer, all you can see are the nails.
In my experience, people pick and choose what they need at the time. I don't think it's unreasonable to say conversations like this happen all the time.
Dev1: Hey, i need to test my control flow, but i need the register allocator.
Dev2: Oh yeah, I put together a facade for register allocation that just does LRU and injects the push/pop pairs to do the right thing. I couldn't get away with a simple interface because there's some weird shared state. The graph coloring allocator is hard, and will take me some time to get ready.
Facade is kind of code for a leaky abstraction. It communicates a lot about how the api really works. Most of the stuff in GoF got picked over and teams took what they needed. Are you doing design patterns? well, kinda. In that sense, i think it did a lot in the Christopher Alexander spirit.
The original author wasn't wrong, just look at FactoryFactoryBuilderFactoryMadness from java's crazy times. But i don't think that's a fair characterization of the entire field of programming.
And it's wrong. No modern programmer does that any more.
When I first came here 9 years ago people would refer to patterns all the time. But this is the first time I've seen the GoF even mentioned in a year or so. People stopped caring about design patterns years ago because they stopped being relevant.
And good riddance, the whole thing was a farce that spawned factoryFactoryFactories and newbies constantly asking how to implement a singleton because actually they wanted glorified global variables because they can't program properly.
Where the GoF completely failed, javascript, ironically, saved us (with Crockford doing the nudging). It forced functional paradigms, lambda expressions, anonymous types, function objects, etc. into the mainstream languages where lisp had failed and almost overnight made design patterns obsolete. It fundamentally changed how we programmed so that the problems the patterns aimed to solve weren't problems any more.
This is the way of the world, design patterns are intended to offer a language, and languages evolve. The uniqueness of design patterns to certain domains/languages can be seen as equivalent to the jargon between different technical fields. It doesn't mean that one set of jargon is obsoleted, it just means it's inappropriate. The failure, if any, in our world (programming) is to recognize and emphasize that different levels of programming, different languages, different operating systems, require different jargon and patterns and approaches.
Number one on HN at the moment for me is "Async/Await support landed in Firefox Nightly". With Async/Await, instead of explicitly queuing callbacks in the main event loop and then returning control to the main event loop (ala Reactor Pattern), I create a queue of callbacks that are scheduled to run one after the other when each one terminates. This is different from a Continuation, where I queue my current state, schedule a callback to be run and then restore my state.
I often use Rails (not usually by choice ;-) ). Data is mapped from relational tables into objects using a pattern known as Active Record. In this scheme you generally have one model object per table and the model object itself is responsible for writing data to the database. There are other schemes for mapping relational data to model objects. Active Record is very convenient for simple data models, but is not great for more complex systems.
The opinion you express (that modern programmers need know nothing of patterns) is one of the reasons why programmers don't understand the tradeoffs between Reactor, Async/Await, and Continuations. It is one of the reasons I have to pull out my hair dealing with domain object models that mirror relational layout exactly.
I haven't mentioned a single pattern from GoF, but it's all the same. These are design techniques that are appropriate in some situations, but not in others. Even the very, very simple design pattern in GoF are unknown by a large proportion of programmers out there. Things like Flyweight pattern should be basics for any programmer, but most people haven't got a clue. And if I had a nickle for every time I've seen Composite Pattern where one of the terminating objects is not a subclass of the container (with obvious hilarity ensuing), I'd be a rich man.
How many times have I seen people deriding OOP and espousing the advantages of pattern matching? We can write functions that are polymorphic to their parameters so that we can group all the functions doing a particular thing in one place, regardless of type! The same people will chop my hands off if I mention Visitor pattern.
The paradigms you espouse are patterns. They even have names. And they have situations where they are appropriate and where they are not. If you get it into your head that the language designer will tell you how to do it, so that you don't have to know, it will limit your growth as a programmer dramatically. If you think that your programming culture will somehow allow you to escape the need to think about and discuss techniques -- using names, and describing situations where the techniques are beneficial and when they are unwanted -- then you will never understand what you are doing.
I don't for a minute believe that you intend to stay so ignorant, but the words that you type have been used to keep many from improving their abilities beyond basic repetition.
`The opinion you express (that modern programmers need know nothing of patterns) is one of the reasons why programmers don't understand the tradeoffs between Reactor, Async/Await, and Continuations. It is one of the reasons I have to pull out my hair dealing with domain object models that mirror relational layout exactly.`
I actually rather a domain model that mirrors the relational layout exactly, as this reduces my time spent in some god unknown framework who is over-engineering with `design patterns`.
`These are design techniques that are appropriate in some situations, but not in others`
Like what? Seriously? Do you have any evidence to backup the assertion of design techniques that are appropriate for some situations but not others? Otherwise you're just kidding yourself.
Really? This requires proof? I would have thought it was self evident. Take the Flyweight pattern mentioned previously - appropriate when you have thousands (millions, billions...) of entities and memory is constrained.
You've listed one example of Flyweight, but without any sources to backup the claim it's appropriate when memory is constrained.
To use the building architecture analogy FTFA, the "door" design pattern is good for some circumstances and the "window" pattern for others. This is the same thing. Not sure what proof you're after?
It's a great collection of patterns that you can pick and choose from, not the be all end all, and totally worth reading even if you only get 1 idea for how to solve a problem from it
i'm going to go with the notion that you're genuinely curious.
I worked on a project with grids, an editable table like an excel sheet. Each grid, row, column, and cell could be styled. Font as Arial, Comic Sans ...; Color as rgb; padding top, left...; background color as rgb; Alignment as left, right or justified; format as freeform string, integer, float money or regex; decimal separator as . or , ; thousands separator as . or ,; and a whole bunch more.
The first cut just naively associated a style struct as part of each cell's data. Several customers complained that grids were very slow to start up, a few complained that they were running out of memory. They were using very large datasets for the grid, and the naive implementation wasted a lot of time on initialization, and a lot of memory on duplicate representation of style.
In retrospect it was a stupid mistake to make. We'd only tested against small amounts of data on relatively fast machines. Customers were using millions of rows.
The solution we went with was the aforementioned pattern. it saved a ton of memory and a ton of time avoiding all of that initialization. There were a few rounds dealing with smart lookup of style based on cell, row, column, and overall table.
My c++ is rusty, but i'm a little baffled by your confusion. Iterating over data structures in a uniform was was kind of a big deal back in the day. IIRC, implementing preorder, inorder and postorder iterators for a binary tree was homework. Have iterators fallen out of fashion?
I don't have a peer reviewed paper paper indicating flyweight is helpful. I don't have a peer reviewed paper saying multiplication is better than looping over adding m to itself n times. It's just better.
Some of them are mostly stupid, ala singleton. Of course even stupid singleton is a nice place to stick environment variables, because they are globals. I never thought decorator was all that great. I really like composite, but i like tree representations that i can recurse over.
If i had a scale, where you could place code on one side, and a design pattern on the other side, and it would balance if adding the design pattern was worth it, i would give it to you. To my knowledge, no such thing exists.
If you have questions, i'll try to answer. here or jfoutz at gmail.
It's hard to say what you mean by this. I have difficulty believing that you think that a technique used in one situation is always going to be the best technique to use.
As someone has already graciously described situations where Flyweight can be used, perhaps something simpler than that like the Singleton can be illustrative.
The OP opined that the Singleton is only useful to people who don't know how to use a global variable. If that were the case, it would not be useful at all. In fact, it's usually something to stay away from. However, it absolutely shines when you want to make sure that an entity's constructor is only called exactly once.
There are many situations where you need to do this -- often when interacting with hardware, this kind of thing is crucial. As the OP suggested, though, it's easy to misuse this pattern.
From a sample of one i.e. me it might be that they aren't forgetting slides 8+, it could be they just didn't bother getting that far.
I got as far as the Iterator example where he, effectively, says that there's no such thing as an iterator pattern because perl has a list iterator built in. Having read that, I didn't feel a huge drive to continue reading the rest.
I appreciate that's probably a failing on my part, it is unlikely to have been a huge drain on my life and I might have learnt something. But that was the path I took. I have a funny feeling others may have done the same.
EDIT: I have felt sufficiently guilty that I have read the rest. If I understood correctly, the author doesn't think that GOF design patterns are the same as Christopher Alexander's design patterns. He thinks that Alexander's patterns are important and applicable to software. He doesn't really say why.
It wasn't a huge drain. It wasn't particularly informative. The Iterator pattern is definitely a thing. Don't feel a pressing need to read Alexander's book again.
Christopher Alexander patterns are about the conversation between the maker and the user; GoF patterns are about the conversation between the maker and their tools.
If we drop out of the conversation about design patterns altogether and look at that kernel of the argument, I think there's a very valuable point there: makers need a way to effectively communicate with their users at the correct level of abstraction. This generally necessitates a design language that is useful to both parties.
Users don't care if their data is being stored in a relational database, or document store, or flat files; they just care that it's persisted. Linus Torvalds didn't build git because he wanted a content-addressable tree of objects, he wanted fast, distributed source code management with protection against data corruption.
Talking to our users is one of the hard problems in software architecture.
But I also think the author's getting at something slightly different - it sort of goes back to that popular barb that design patterns are just a way to make up for a language's shortcomings.
At the time this was written, doing something like foreach in most languages was a horrible PITA. And if you had a language like foreach, and you were watching people use the iterator pattern (which, I realize, is more powerful, but mostly it would get used for stuff that foreach could handle), you'd definitely shake your head sadly to see all the sweating it would take to express simple concepts, and you'd think, "Geez, why are most programming languages so awful?"
A lot of languages stayed awful for a long time. Java only recently got better, I think partially in response to pressure from .NET. Nowadays, though, even C++ is a wildly different beast, so the point the article was trying to make has lost a lot of its poignance.
You absolutely nailed that feeling. And yes, it is indeed useful to have a repository (he) of patterns, just so you can start throwing them at the problem until you find something that sticks.
Edit: I've been wanting to read A Pattern Language for a while, but this snippet from Amazon made me chuckle. If I didn't know better, I would have given it a 50/50 shot of being from APL or GoF!
>The elements of this language are entities called patterns. Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.
Design Patters are simply tools at a higher level of abstraction than the code itself.
They are 'approaches to modelling information'.
That's it.
Use them, hack them, or not.
I've found them handy enough, though never implemented anything precisely. They make sense when communicating ideas as well.
I don't see why there is a need for debate.
I believe there is some element to aspiring to use them though. Once something becomes common enough to be called a design pattern that 2+ independent developers might recognize, that's the signal of a common problem that might have better solutions than the first couple you come up with in planning.
If a design pattern becomes really common, perhaps enough developers get together and add a feature to a language (or make a new one) to encompass what was once a common bit of spaghetti code.
In C, you'll sometimes see people make a struct containing only a collection of function pointers, and different parts of the code will produce instances of the struct with the function pointers populated differently. In languages with built-in polymorphism, this isn't necessary.
Typeclasses often remove the need for facades.
First-class constructors (e.g. static virtual methods) remove the need for factories.
The slides' point seems to be that there's this other concept, which is that each project should develop a language for speaking of the project, perhaps more specific to the project. But also not just a list of "things people use currently". Your new projects could include completely new design patterns.
So the idea when starting a project is :
when designing, define concepts that you will use a lot. For example, you might be using a lot of iterators. But for the designers of Angular, you're working with promises, with controllers, with scopes. Use this language when trying to frame problems, and people will build off the same base.
When people start coding, everyone starts off the same foundation. But that foundation doesn't need to just be "the design patterns from all over". You can have your own design patterns, more specific for your project. Have your best developers choose the right design patterns for the project, and even the most junior developers will be guided by this when working on the details.
1. Modern (not necessarily just modernist) architects are generally terrible at achieving what Alexander refers to as "living space" because they're solving for the wrong things.
2. Small-scale patterns and large-scale patterns can constructively or destructively interfere with each other from urban planning right down to construction details of a room.
3. If you've really mastered design, you can leave behind the pattern language and cookie-cutter implementations: your solutions will organically develop with these large-scale/small-scale synergies.
I'd suggest that GoF is a little closer to Alexander in these respects than the presenter gives credit for, particularly for point #3. The book studiously makes the point that its implementation of these patterns is not meant to be the only way to implement them. It also makes no attempt to be an exhaustive catalog. The fact that some of these patterns have standard and different implementations in other languages (e.g. iterator method hooks and generators in Python) doesn't necessarily mean that the pattern itself is useless outside of its OOP niche.
I'm glad WikiWikiWeb is back up so we can debate all of this on the one true forum for debating design patterns. :-)
https://en.m.wikipedia.org/wiki/Constraint_satisfaction_prob...
For instance, MVC is a very popular design pattern. When you read code built according this pattern, even if you are not familiar with the language or framework, you know what kind of thing to expect to find in different parts of the program.
They can also be useful in a context where not everyone in the team is used to writing code in a disciplined way - like coding standards and ideas like YAGNI, they can give an objective set of standards at code review. Adopting a few of these strategies in a code base provides a standard people can refer to about what goes where. Where the question is whether James should have stuck this database code in the class that renders the web page, being able to point to a well documented strategy and say "that code doesn't go there because we're doing that" can be better than excessive conflict.
The author is right that if you have a common problem, you should probably be solving it with a library (preferably someone else's). However, often these libraries and frameworks are implementing the generic bits of the design pattern, and understanding that pattern gives you a shortcut to knowing how to use them well.
The GoF patterns give everyone a common vocabulary to reason about the patterns.
The problem with MVC is that it lost any accurate meaning.If you tell me "my app is MVC", I vaguely understand it has a view, a controller and a model but it doesn't tell me if the controller sits between the view and the model or the view directly observes changes in the model.
If you take a pattern like the Chain of Responsibility, or the Command pattern, the goals are clear because they are usually made of a single or at most 2 collaborators. MVC is sometimes a mix between the strategy and the observer, sometimes it isn't yet people still call that MVC.
I've seen applications that control hardware where a class called 'model' twiddles bits on an interface port and then blocks until it sees some bits twiddled in response. Or where the 'controller' actually goes off for an hour to do heavy data processing (using the web servers thread pool as a job queuing system).
In both these cases, those things should have been in descriptively named classes that accurately described what they did. In the latter case, trying to adhere to a pattern gave the application a strange architecture that could have unforeseen problems.
I'm not sure if MVC is even the right pattern for a lot of modern web applications with a thick client or that describe a workflow. Often it feels like you are shoehorning there.
However, I'd say these are cases of 'doing it wrong' that don't prove the overall approach bad. I picked MVC because everyone has heard of it.
I think design pattern books would be more helpful if they deleted all the pattern names and just called the book "A collection of a-ha! moments in C++" or whatever. Readers could skim through the chapters every couple of months until they stop having a-ha! moments. The "patterns" may or may not emerge in the reader's code, depending on what works best for a specific project.
I've seen a big project with heavy emphasis on patterns, and it was a hell for new programmers. Take a month debugging 50 levels of method calls trough the facades and the framework, just to find code that does the business logic.
Since that point has been established, it would make a phenomenal follow-up talk to take Alexander's book, which is from the 1970s, and begin a discourse on how his ideas can be applied to modern software engineering and architecture. While the reader takes away from the slides that Alexander has some unique ideas that are probably useful, he/she doesn't come away with a good understanding of what they might be. (though if anyone knows of talks that discuss Pattern Languages, I'd love to see them!)
He goes on and on to put down C++ and the four book authors as if he holds a personal grudge against both. He makes it seem as if C++ programs were written by code monkeys pasting examples from books in order to make it work while other programming languages are god's word. It does not occur to me why he thinks that in C++ there should be a general solution for an iterator over any kind of collection^1. Yet C++ allows to implement data structures on the lowest level and only the programmer may know how to iterate over a certain multiply linked structure (if possible at all). The Perl counterexample looks like a straw-man to me.
I agree with you, the core of the talk comes after the talk and is not presented properly.
1: For standard collections in C++ there are iterators predefined and you can iterate over the collection easily.
The bigger complaint about software patterns is that, unlike Alexander's original patterns, they are merely tools useful to build any kind of program. Alexander's patterns were meant to only be useful in building humane places with humane processes. You literally could not build a mass prison with the construction methods he describes, because they aren't amenable to mass production, design from afar, or economies of scale.
Exactly the opposite is true of our usual application of "software patterns". They are often used to produce software that is inherently immoral. Alexander's patterns describe an environment of decentralization of decision-making about both architecture and the community it inhabits. Meanwhile, our "software patterns" embed a centralized power structure both in the code (architect->team lead->junior programmer) and in the world now increasingly controlled by the whims of Silicon Valley founders.
I wonder what it would look like to describe low-level primitives of computation, similar to Alexander's new low-level architectural methods, which actually made decentralization of power a reality and resisted economies of scale. They certainly wouldn't look like the Gang of Four patterns.
None of these things is obvious to me. In fact, this whole paragraph veers in the direction of a conspiracy theory. The rest of your post is quite interesting and I agree that the design of our software should tend towards the human and humane.
> Exactly the opposite is true of our usual application of "software patterns". They are often used to produce software that is inherently immoral. Alexander's patterns describe an environment of decentralization of decision-making about both architecture and the community it inhabits.
You should expand on this.
His critique of architecture is this: the quality of a building comes from a process which has to unfold over time and take into account very minute preferences of the kinds of people who will actually inhabit it. A good house design can't be measured by the plans, but only by living in it and seeing what kind of life occurs to the people who are there. Therefore, if you build it by one person's design, or because the renderings look nice as a 2d composition or as a 3d model, or to reduce costs by repeating the exact same things over and over, you will necessarily end up with a bad building. It won't be bad on purpose, but the power structure of the process that built it will have made it bad. (Maybe an architect wanted to win an award, or a developer wanted to sell the properties quickly without worrying about the owners' long-term experience, etc.) And if you want a better building, instead of arguing about design, you should just find a process where more of the decision-making is made by the kind of people who will inhabit the house.
The same is true of software. I do not think most people set out to make bad software. But Conway's law tells us that the result will reflect the organization and process by which it was created. If the process of creating the software is embedded in a large corporate structure where VPs are fighting, that politics is necessarily felt by developers and even embedded into the system. If the process of creating the software is embedded into a VC unicorn and its only chance of growth is to destroy local communities or mistreat workers on a large scale, that will happen.
This is even true just for the people building the software. I've worked at companies where the design team had the power, where the engineering team had the power, or where the project management tool had the power. The same people might work with the same goals, but in these three environments they will produce three very difference pieces of software. And I've worked at companies that had the power to trick money out of customers, which soon replaced the desire to actually provide customers any benefit, and produced its own unique form of engineering environment.
Alexander's critique is aimed at the very idea of "scalability": any one person or process who can determine the way architecture (or software) behaves for thousands of people will get it wrong. Not because the person is bad or the decision is inherently wrong. But because no one decision could possibly be right for all of those people.
Being new to Java at that time, the GoF patterns were also a very useful crutch to me when trying to learn to "walk" in the bright and futuristic world of class based OO. And it provided me with a common vocabulary that would ease communication with coworkers.
These days I find the GoF patterns to be a bit dated. A lot of it had to do with patching the shortcomings with the popular languages of the day; mostly C++ but Java had the same kinds of issues. A good deal of them could be paraphrased as "alas, functions are not 1st class citizens but as a workaround let's wrap them in an object" or "alas, our language does not readily support the common need to do X, so here is how to implement X ourselves". Someone once noted that design patterns are really language smells and I think that hit the nail on the head. The GoF patterns are really a catalog of the pain points of C++ (and similar strongly typed, class based OO languages).
In recent years I've had the pleasure of working in Javascript. Does GoF apply to Javascript? Not really. Does Javascript have design patterns? I'd say yes but there are fewer of them. Promises are really a design pattern to ease the pain of working with callbacks. Using function scope to hide private variables is a design pattern to deal with the lack of having private sections of the code. The module systems such as CommonJS deal with the lack of language level packages, and also provides a solution to isolating code in a well defined way.
Bottom line is that design patterns are not universal in any way; they are very specific to the type of language that they deal with. Also: Language designers better pay attention to the design patterns which evolve over time since each one points to a potential improvement of the language itself.
The only "solution" to this question in software that I'm aware of is, "hire a charismatic architect and let them do all the design" which is terrible.
Thinking about my team, I'd say probably a third of them are interested in architecture at all, and of that third, at least one has a punishingly minimalist aesthetic that the rest of us loathe. The rest of us are fairly scared of being wrong, scared enough to avoid making bold decisions.
GoF may be insufficient, but it's really the only pattern language we have. I don't even know how to think about this idea properly, which I consider a symptom of the author being onto something.
The hardest thing for a lot of people to hear is, "you're not wrong, but the thing you're thinking about isn't the essential one." Architecture is about finding the principles and axioms that apply to the situation. It also requires an ability to "let go," in that if your design is good enough, you are no longer personally necessary because the rest has been made obvious, and anyone can implement it.
Elegant thinking can seem obvious, and transmits a great deal of understanding simply. The purpose of architecture is to formulate something that benefits in scale to the efforts of multiple cheaper, less experienced people.
Thanks for elaborating on this.
I will email you privately about it.
https://archive.org/details/TheTimelessWayOfBuilding
https://archive.org/details/APatternLanguage
It's also worth reading Patterns of Software by Richard Gabriel: http://www.dreamsongs.com/Files/PatternsOfSoftware.pdf
who draws heavily on Alexander's work.Citation needed.
GoF's intention was to give people a tool for thinking about architecture and sharing their experiences with others. If people are "programmed to implement them habitually", that's some manager's fantasy and neither in the letter or spirit of Design Patterns.
It's be like seeing a programmer blindly use jQuery without understanding JavaScript, and blaming it on jQuery instead of the programmer. A tool that can be used badly, even one that lends itself to being used badly by a certain population, doesn't automatically mean the tool itself is rotten.
As others have pointed out, the book is nice because it gave rise to a common language for describing patterns, and it's much easier to understand code someone else wrote if you can divine intentions from variable and class names.
The issue is that in a lot of shops design patterns have turned into a religion and not just a tool to help thinking. I've seen managers like that.
If you didn't implement 15 different design patterns in this or that code base that was "bad design", even if it unnecessarily made the code more complex.
I've seen iterators classes written when a simple for loop would have done the trick. I've seen chains of responsibility implemented when a basic switch statement would have been enough. Of course the code wasn't meant to be refactored in anyways. In fact complexity led to inability to refactor anything as nobody had a clue what went where ... and of course to manage all that bullshit we had to write tons of factories, because instantiating a single useful object now took 30 lines of code ...
Good programmers know the importance of YAGNI and the root of all evil that is premature optimisation. They should be well equipped to avoid over-engineering while still getting the learning benefits of patterns.
Chain of responsibility, for example, is a pattern I learned via GoF, have found it to be useful on several occasions. I quite possibly never would have known about it otherwise. At best, it would be left to chance for me to stumble on it or refactor into it enough times to recognise the pattern.
More widely, addressing the slightly snidey remarks about C++, which form the basis of most attacks on design patterns - this idea that design patterns are snippets of code certainly did take root, and persists to this day. Even with DDD, people missed many of the more interesting 'soft' factors addressed, and walked away from the book thinking all they needed to do was rename their data access objects 'repository'.
But it's utterly useless to say that iterators as a concept are pointless because foreach exists (and whether or not the author wants to believe people are missing the point, this is the foot they launch off from). Languages with first class functions are popular today, and I still hear that you don't need the strategy pattern because of this. And yet I look at Python's urllib (literally today), and its retry mechanism is configured by supplying a value telling it what to do. It's doesn't expose an interface to make the decision yourself. Design patterns in software (whatever the ancestry of the phrase) give you a better granularity to discuss, think about and plan software design. Sure, we have more retrospective patterns, but that's as much to do with the fact that we make a ton more software than architects make buildings.
I certainly agree that we haven't, as a community, got full value out of even our own interpretation of design patterns. But I don't think it's because of any of the issues described in this talk, and I certainly don't think it's because of the limitations of our programming languages.
If you read Alexander's interviews in Stuart Brand's How Buildings Learn you'll see he's mostly concerned with how to connect people directly with the problems his buildings and urban planning are trying to solve. It's highly cultural. He rails against what he calls "magazine architecture" where buildings are designed mostly to photograph well, their inhabitants's problems with the buildings are an afterthought. Sounds familiar?
I wish I had the latter in my hands now for reference (it's at home), but from what I can recall, the book goes into great detail about how to solve specific problems using generalizable solutions. For example, to establish a connection between the space inside of a building with the space outside of the building, consider adding awnings with open archways to the perimeter.
The Design Patterns book, though wildly specific (possibly only because it can be), also deals with solving specific problems with generalizable solutions. For example, creating and ensuring only one instance of an object exists (Singleton - p. 127). The book outlines the motivation for this (e.g. the problem), and defines an abstract for the solution.
Also, the fact that more modern languages can more easily solve some the problems discussed in Design Patterns is, while true, seems like a bit of a red herring. Undoubtedly the patterns themselves have a lifetime of usefulness, and will evolve as we discover new ways of solving new (and old) problems.
The article demonstrates that fact pretty well.
It's also a good way to judge how practical a language is. I don't need to write iterators in a language that has generators for instance. I don't need to write factories in a language that supports closures... But then people selling all these expensive modelling tools would be out of business ...
But these pattern catalogs present a risk of seeming complete. To believe that you're near completion on a list of patterns in any field is utter hubris. Alexander's own hubris at having discovered objectively "correct" architectural styles led him down a path towards obsolescence while the rest of the architectural discipline moved on. The hand wavy "scientific" surveys Alexander conducted wouldn't pass a basic social science methods review these days. Pseudoscientific methods of assembling anecdotes or interviews from small samples that don't control for sampling bias will always result in some interesting insights that can't be reliably generalized beyond the narrow scope from which they were gathered. That's what both of these pattern languages are. At best they are a handful of neat ideas that other practitioners can reuse. But these are not some sort of reliable canon. We're all going to have to do more rigorous research if that's what we want.
There might be another parallel in here about the fetishization of anachronisms. Alexander, who is an architect not a carpenter, goes out of his way to glorify hand-crafting things (literally avoiding power tools). Unless you're a carpenter who specializes in make "artisanal" works of art for wealthy patrons, I guarantee you'll enjoy the productivity gains of a pneumatic nail gun. So if you ever find yourself insisting that some old "pattern" is more pure at the expense of everyone else's productivity, double check whether your dogma is worth it.
TL;DR. Don't take patterns too seriously. Always look for new ones.
We don't often understand what we are building most of the time.
Software is a communication tool most of the time. You are communicating between machine and user or user to machine to network of data and so on.
Most of the code is really a translation from input to some kind of command or process and outputting the result.
The more layers you add to that, the further you are from the truth and the more confusing the world truly is. For all the patterns, abstractions, and so on that we lean on, we miss out on the most important one...
input -> processing -> output
Indeed. Programming is a process of constant discovery and reframing of the problems ahead.
> Most of the code is really a translation from input to some kind of command or process and outputting the result.
I disagree. This may be true in a general form, but a general form like this is of little utility. There are many types of programs for which the focus is state manipulation, and what you care about is that state - whether in its final form (e.g. 3D modelling software) or in its evolution (video games). The classical output here is just a snapshot of state. I don't think trying to squeeze everything into a "dataflow" paradigm of input -> processing -> output is a good idea.
Also, in context of design patterns - the problem is that even if we take "input -> processing -> output" assumption, the space of possible outputs is huge, and the criterion for acceptable outputs is very complex. It is this complexity that makes our code complicated, and the reason we have to build layers of abstraction is because our minds are pretty limited and we can't handle much complexity directly.
There are two kinds of languages: languages that have design patterns and languages that don't have a lot of users.
J/K'ing. But he consciously chose to give the talk a "click bait" title, and the goat thing was a bit of hyperbole. Totally reasonable things to do, but know that you have to live with the backlash from that.
All of that said, I agree with this core point that "we need to take a fresh look at Christopher Alexander". I plan to go out and get a copy of the pattern language book and read it sometime soon, to get a better perspective on "real" Alexanderian patterns. This was a handy reminder to do that.
(I would recommend reading or skimming through "A Pattern Language" first, to get a good idea of what Alexander's idea of a well-formed pattern is. It's also a really interesting book in its own right, especially if you have any interest in architecture.)
My understanding is that Alexander's patterns applied to software would be the typical components of a software system (DB, queue, API, etc). The GoF patterns are about how to build those components. They are lower level.
We need both, but don't we already have both?
Alexander is talking about 'spaces' in buildings, how they take shape, how they interact, and how they serve the greater style and purpose intended by the master architect to serve the lifestyle of the resident. Here design focuses on the end product and how it will function for the user. These design patterns have nothing to do with how the building was constructed or the experience of the tradesmen who built it.
In contrast, the GoF book did not talk at all about the final software end product, but only the process of building it. The emphasis is entirely on the tools of the tradesmen and encouraging their proper use and re-use. Such design patterns do not reflect the user requirements or even the architect's high level design of the software product, much less serving the user experience in a consistent purposeful way.
However it seems to me that Alexander's approach to patterns could have been adopted in the software domain, but I think that would have looked a lot more like Jef Raskin's style guide at Apple which encouraged developers to adopt a common holistic yet systematic approach to Mac software so that the user experience was served. I think Raskin wanted apps to fit together the way that rooms in a well designed house flow from one to the next -- something Alexander would endorse, I think.
Another variant on design patterns in the software that might have worked well, at a level that might have bridged Raskin to the GoF, could have been Alan Kay's model for objects. The higher level conventions for components and behaviors that Kay espoused might have made for an interesting basis for patterns like those of the GoF. But without Kay, much less Raskin, the very modest ambitions of today's notion of software design patterns led nowhere but software reuse -- an inauspicious end to Alexander's vision, IMHO.
I feel like GoF was pretty useful in a moment in time where a lot of people were facing similar problems and could benefit from canned solutions to those problems. For the most part are not the problems that I'm trying to solve today aren't really related to those monolith UI application and the tools that I use have some patterns already built into them that I tend to follow. So seems it's something that's useful to study, in the right context, learn something from, but not necessarily copy/paste patterns from.
> The pattern language does not tell you how to design anything
> It helps you decide what should be designed
> You get to make up whatever patterns you think will lead to good designs
I'm not sure I completely agree with this. I agree Alexander was trying to establish a vocabulary for communicating common ideas, and I agree that his collection of patterns wasn't meant to be the one true canonical list of architecture patterns, but I do think a lot of his patterns do specifically tell you how to design things. Also much of what he wrote isn't specific to large construction projects with many stakeholders ("The Oregon Experiment", which I haven't read, is an account of the process of designing the University of Oregon campus. However, "A Pattern Language" would also be of use to a person designing their own house by themself.)
What makes A Pattern Language more interesting and intellectually honest than just a list of design rules is that he gives justifications for each pattern and tells you what problem they solve and why he believes them to be an optimal solution. Anyone else can come along and propose a better pattern that solves that problem (or a more general one) in a better way, or can provide some argument that the problem isn't really a problem in certain circumstances. There's no appeal to authority or tradition; patterns are judged solely on their merits (to the extent that we can understand what those are; architecture is strongly tied to psychology, which isn't entirely understood).
(I really wish that someone would write a book similar to A Pattern Language, but about music theory.)
Experts learn to think and act more efficiently by forming "chunks" - associations between different concepts that are recalled together and can be handled as a larger unit (which reduces the amount a person needs to keep in working memory).
I think this really has a lot in common with "design patterns", which are basically "chunks" of design expertise that have been written down and can be recalled by name and shared between people.
(Also, if you haven't looked through "The timeless way of building", do it! It's a beautiful book about architecture and the role of individuals as a part of society and how our environment shapes our lives. When I open it I feel transported to 1970s Berkeley.)
https://en.wikipedia.org/wiki/Composite_pattern
https://en.wikipedia.org/wiki/Algebraic_data_type
Good type systems are a trade-off between complexity and expressiveness, but it's something that is hard to get right. Obviously Java's type system is simpler thus in theory easier to learn. But in practice F#'s is easier to use if understood and might lead to smaller thus more maintainable code bases.
1) Iterators are pointless because perl has foreach. (Which entirely misses the point of why you'd want to abstract the concept of iteration from the container. (Hint: try using foreach on a tree.))
2) A bunch of trash talking about other languages type systems. (Incidentally, even when this was written, those criticisms were untrue.)
However..
3) According to the postscript, we should ignore the bogus parts I just mentioned above and just focus on the parts that are right. I'm guessing this is the "throw spaghetti against the wall and see what parts stick" school of persuasion, where you can just say a bunch of shit that's obviously wrong and then tell people to ignore those bits.
So basically, the point of this talk is "go read Christopher Alexander's book"
I remember this whole talk now. I actually have an acknowledgement credit... holy crap. That was a long time ago.
Here's how I see GoF: it was a brand new idea to programmers. GoF was trying to open some eyes to the idea that there were already all these patterns in a lot of software. They called out a few of them. Some are really classic. Some were total misses (how many of you have implemented State?)
GoF was also coming from the Smalltalk world and trying to preach what they'd learned so C programmers could understand it.
So in 2002 when MJ Dominus wrote this, a lot of us were feeling a lot of stress about the rise of Java and C#. A lot of the appeal of these languages was the promise of never having a memory leak. A side benefit was that Java especially didn't enable some of the really risky C++ practices (MI, overloading short circuit operators... basically most of Scott Meyers' "Effective C++" books). But if you were any good at C++, you gave up a lot. Generics were the big pain but I remember at the time really missing the STL <algorithm> library.
MJ was (is?) a Perl / dynamic languages guy. There was vigorous discussion in the "blogosphere" (gag) over the static/dynamic language debate. MJ was basically saying to think bigger than these trivial little patterns. GoF is to software architecture is like nails & drywall are to an architect.
To the degree I understand Alexander's central answer, it is "Put the needs and wants of humans who are the users of the design at the center of all design, planning, and building (and heve them do the design, planning and building, where possible.)" If that's close to true, then making things that are programmable is more important than 'getting it right the first time'. One can always take a chainsaw or an editor to a bad decision.
You can build an abstraction where a car as a particle for a thought experiment or where a car is 3d vehicle with a deformable chassis for crash simulation. Both can represent a vehicle to certain level of detail. The correctness of using each level of detail is very context sensitive.
To model a problem after a design pattern can have a lot of overhead, but it can objectively produce a working solution.
Now, if the design pattern is really a way to solve a problem in terms of constraints imposed by a type system or a language, once you add, change or remove those constraints the problem solving approach might stop making sense or being optimal.
If you like to check it out, you can find it here: https://shirazian.wordpress.com/2016/04/11/design-patterns-i...
To be fair, it is far from the only source of motivation to see programmers as (or turn them into) a fancy macro processor. Maybe that's one reason why shops where discussion orients heavily on design patterns often also seem to be places where Management Theory X [0] is on display.
Concepts are supposed to be the Design Patterns in the sense mentioned in the slides. But they could use more user friendly names. (no, I do not mean developer friendly)
for elem in collection:
and the `__iter__()` function. Correct?only perl6 supports lazy lists and coroutines and yield.
but iteration is only a trivial part of a design pattern. as the name says, a pattern is matching certain conditions, like number of arguments, types of arguments, context, ... and applies a specific solution to this pattern. perl5 and perl6 are not functional enough to provide this kind of pattern matching and functional dispatch. python and ruby neither. perl6 has multi-methods, but no proper matcher.
Norvig explains a bit in http://norvig.com/design-patterns/design-patterns.pdf, but in essence any strongly typed FP deals with that.
> but you got no language support as with __iter__, other than using tie callbacks.
What are tie callbacks, if not (creaky) language support?Also the perl way would be to implement infinite streams, ala MJD's HOP[0].
[0]: http://hop.perl.plover.com/book/pdf/06InfiniteStreams.pdf
Impressive insight.
A universal construction, at the vaguest level is the most general way of solving a particular problem. For example: What is the universal way to construct a program that produces two values?
Via curry-howard: A program is an arrow from f : A -> B. We call A and B types, they are like specifications of the environment a program takes and the environment it produces. e.g. (+1) : Int -> Int is the program that takes an Int and produces another Int, by incrementing. We also have a way of composing programs, (.), which to young mathematicians is function composition, to older mathematicians morphism composition, and to nixy programmers the pipe operator (in reverse). (+1) . (x2) : Int -> Int is the program that first multiplies by 2, and then adds 1.
We want a solution that produces two values from the same environment. Such a solution looks like f : W -> A, g : W -> B. That is, we have two programs that take the same environment and each produce their own value.
Let's say our language has product types. That is, given a context A, and a context B, we also have the context A x B. This is a very natural thing to have. A language with pairs/tuples has such a type. What makes product types special is that they come with functions proj_1 : A x B -> A and proj_2 : A x B -> B which do the "obvious thing". That is, the first one just gives you the first element of the pair while the second just gives you the second element of the pair. I claim that this type and its projections are the universal solution to the problem of constructing a program that produces two values.
What do I mean by that? I haven't defined at all what universal means. See, I don't want to give the rigorous definition because it is very abstract and I think it takes a lot of pondering to really understand it. Instead, let me wave my hands some more and give references at the end: A universal construction is something that solves a problem in such a way that any other solution to the problem can be transformed into an instance of this construction.
The functions proj_1 and proj_2 are not the only feature of product types. They have another feature, and it is that, given a solution f : W -> A, g : W -> B, there is a unique way to produce a function W -> A x B, that is, there is a program that converts our solution into a product type. This is usually denoted < f, g > : W -> A x B. Furthermore we have that proj_1 . < f , g > = f and proj_2 . < f , g > = g.
This might either be complete nonsense, a triviality, or something you already understand. This definition of product, when made rigorous and with all its details written in, transfers from programming languages, to logics, to set theory, to algebra, to topology, to geometry and beyond. It is a pattern, and you can use this pattern wherever you find it. And where you don't find it, usually there's significant reasons why it can't exist.
A true Program is not really an Arrow, as it deals with mutable unknown state that cannot be reduced to a known set of inputs completely. (Where functional and monadic programming gets awkward or impure.) There may also be multiple outputs and inputs at any stage in the process, an Arrow limits you to one output and input vector making actual programming awkward. Forces the kinds of composition you can describe.
Computer software is a form of literature. The most important thing is how well the language of this corpus persists. That only happens through usage.
Thus, by slide 6, I was prepared for a "because: Lisp" argument, and .. there it was.