17 ms·
On Design Patterns in C++
- AlexanderDhoore 6y agoDesign Patterns are a massive code smell. I always prefer simple code over extensible over-engineered frameworks.
- happytoexplain 6y agoYes, but this approach doesn't scale.
- dvfjsdhgfv 6y agoTorvalds and a few other people would disagree with you.
- BeetleB 6y agoThe reason it's just a few other people is an indicator that it doesn't scale.
- ptr 6y agoSo you would rather have a “sortListByName(list)” where the sort algorithm is hard-coded to what field we should compare on rather than a factored approach using the Strategy pattern? “sortList(list, [](const item& i) {return i.name();})”? I would say the factored solution using “Design Patterns” is the simple one here.
- akavi 6y agoI'm not sure I agree with the author of this book that that counts as an example of "The Strategy Pattern" (vs. simply "passing a function as a parameter"). Is having parameters at all "The Parameter Pattern"? Should we call passing a boolean flag "The Flag Pattern"? To me, "Design Pattern" implies at least some higher order structure than that (and which is not natively abstractable over in the language in question).
- nesarkvechnep 6y agoI have a colleague which calls a higher-order function Reader pattern which is obviously absurd.
- aparsons 6y agoThe Flag Antipattern
- ptr 6y agoThis is the crux of the matter. Design patterns are just common structures that have been found useful at some point in some context. Some programming languages have adopted these patterns, adding "native" support for them. If your programming language didn't have parameters, then yes, I guess someone would add the "Parameter Pattern". It's all rather arbitrary what ends up in a book on "design patterns". Regarding the strategy pattern, when does it start becoming the pattern and when is it just "passing a function as a parameter"? sortList(list, [](const item& i) {return i.name();}); struct MySorter { bool operator ()(const item&) {...} }; sortList(list, MySorter{}); class MySorter : public ISorter { public: virtual bool execute(const item&) override {...} }; sortList(list, MySorter{});
- thom 6y agoDesign patterns aren't common _structures_. I suppose this is why everyone gets so caught up in the language specifics and think they're trivial. Design patterns are common _design decisions_. The strategy pattern isn't saying "you can have callbacks", it's saying "sometimes you should allow a policy to be supplied by the user". Every day, in every language, even the ones with first class functions and people posting on the internet that design patterns aren't needed, people still use libraries with massive 'options' arguments to functions, instead of internalising the actual lesson of the strategy pattern which is to allow executable specifications of behaviour. So yes, there are trivial things which you can argue the toss on, but in the last month I've used HTTP libraries that don't allow you to supply a strategy for retry logic and machine learning libraries that don't allow a strategy for early stopping.
- jcelerier 6y ago
- TillE 6y agoFor the nth time, design patterns are descriptive, not prescriptive. They're a guide for when you're facing a type of problem which has been encountered thousands of time before, not a blunt tool to be jammed mindlessly into every possible scenario.
- ahartmetz 6y ago...and the elaborate ones aren't needed very often, in my experience. So if you have many of them, it's probably overuse.
- cle 6y agoThe problem is that you can preach this over and over but some people will still use them mindlessly in every possible scenario, because it's easy and feels good. I think we've done a poor job as an industry of beating new developers over the head with "know the tradeoffs!" Design patterns are taught without teaching their tradeoffs, and so new developers think that it's best practice to apply every possible design pattern as eagerly as possible. I agree that naming patterns is useful, but IME they've been a net negative on the code bases I've worked on, since the way they're taught encourages using them mindlessly without considering their costs.
- smitty1e 6y agoI think the languages with multi-page exception dumps are a great example of design patterns applied to the point of absurdity.
- kenniskrag 6y agopatterns are not frameworks. Patterns are not libraries, too.
- amelius 6y agoBut they could be ...
- kenniskrag 6y agoa library maybe but not a framework. The framework controls the program flow.
- skor 6y agounless you're writing a framework
- AnimalMuppet 6y ago"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." - Greenspun's Tenth Rule In the same way, any sufficiently complicated C++ program contains an ad hoc, informally specified, bug ridden implementation of half of the Gang of Four design patterns. They would be better off just using the patterns, instead of trying to roll their own half-baked solutions to the same problems. If you've got one of those problems, use the standard, tested solution. If you don't have one of those problems, don't shoehorn in a solution that doesn't fit. It's not that hard to grasp (except for junior devs who have recently read a pattern book...)
- mdiesel 6y ago"Oh, it's just a singleton" - me after half an hour of reading yet another implementation calling it something else.
- AnimalMuppet 6y agoOK, let me extend my statement. If you've got one of those problems, use the standard solution, and call it by the standard name so that everybody understands what you're doing and how you're doing it.
- comeonseriously 6y agoFunny, I prefer Design Patters over extensible over-engineered frameworks. Design patterns can reduce a lot of unnecessary cognitive overhead. For example, a builder pattern that takes a config a returns an evaluation pipeline is really simple and eliminates a lot of runtime if..else conditions.
- s1k3s 6y agoThere's a balance to it, and finding that is what (I think) you should strive for in software development.
- rualca 6y ago> Design Patterns are a massive code smell. Are you really trying to argue that a collection of terminology aimed at describing high level concepts, or standard solutions to commonly repeating problems and requirements, is a code smell? What other good engineering practices are also code smells?
- msla 6y agoYes, things like thread-local storage and singletons are so over-complicated. /s https://en.wikipedia.org/wiki/Software_design_pattern https://en.wikipedia.org/wiki/Software_design_pattern
- rimher 6y agoIt's very close-minded to believe that having a common phrasebook to solve common problems can be described as a "code smell". Design Patterns are tools in a box, aiming to create scalable solutions, but the way a solution is implemented is up to the implementer. Design patterns just try to establish the language.
- pjmlp 6y ago> The C++ in the GoF book is not representative of C++ Looking back at when the book was published, we had PowerPlant, CSet++, SOM, COM, CORBA, Turbo Vision, OWL, MFC, VCL, Motif++. Definitely representative. I like C++, and yes there is more to C++ than those OOP frameworks, yet lets not pretend that what gave birth to Java like enterprise coding came from C++ development practices during the decade that preceded it.
- skor 6y agoEither you understood these or you didn't, what I like about them is that I don't reinvent the wheel every day. Small differences here and there, but the structure is the same, and years later, I see it and I remember how it all works, real quick.
- kliments 6y agoUseful book, thank you for sharing!
- x87678r 6y agoI wish we had a new Design Patterns book in 2020. GoF book is well obsolete, since then OO has faded, JS & Python are the the most common tools, even Java is unrecognizable with Lambdas and functional features everywhere. Meanwhile vast amounts of memory and CPU power mean flyweights and object pools aren't so useful and immutability is now a valid design goal. The GoF needs an update.
- mumblemumble 6y ago> Meanwhile vast amounts of memory and CPU power mean flyweights and object pools aren't so useful I'd argue that they still are relevant. The only thing that's seen more impressive growth than memory volume is the relative cost of a cache miss. So the great irony is, one of the most efficient ways to sabotage your CPU power is to make use of all that RAM.
- arjun-menon 6y ago> The only thing that's seen more impressive growth than memory volume is the relative cost of a cache miss. Has the cost of cache misses truly increased? (Also: relative to what?) Today's CPUs have far larger caches than in the past (and not to mention a lot faster too), so the number of cache misses and the time cost of each cache miss for the same program on a modern CPU should be much lower—right?
- gjulianm 6y agoI think the parent talks about the relation between miss/hit, not the actual cost of a miss. In other words, if a piece of code took 100ms with 100% cache hit and 200ms with 0% hit but now it takes 20ms with 100% hit and 100ms with 0% hit, your code is 5 times slower with cache misses, when before it was only 2 times slower. Yeah, it's faster than before, but the relative cost in terms of your available performance has increased. And I'd agree with that statement. I have been doing some heavy profiling lately and it's fun to see how good are modern processors at executing even non-optimal code if it's cache friendly. However, the moment it needs to hit L3 cache or further, the pipeline stalls and performance goes down the drain.
- zwieback 6y agoI remember when those books came out and puzzling over them, especially the Modern C++ Design. It's hard to describe what it felt like back then, the promises that these techniques were making, how exciting it was and how differently software development practices have evolved. That's what I love about writing software, even after 30 years in the trenches. There's always something new to discover and then draw conclusions about how it's different from what you thought 5 or 10 years before. I don't know if I would recommend these books today but they are undeniable classics.
- SoSoRoCoCo 6y agoI didn't like this article because it picked two examples out of two 400+ page books just to gatekeep C++. I don't know the author, but I don't see the purpose of this article. There is so much more to C++, and so much more to the computer book publishing industry. The former is hard to capture in one text because it is so radically diverse, the latter floods the market with crap. I really have a problem with articles like this one: paper thin and disposable.
- whimsicalism 6y agoFWIW from what I have read of GoF, I think this criticism about runtime polymorphism is spot on.
- SoSoRoCoCo 6y ago> FWIW from what I have read of GoF, I think this criticism about runtime polymorphism is spot on. Here you are repeating second hand criticism as if it were your own because you have not even read GoF. Why would you promulgate discussion on something you have no first-hand knowledge? Everybody wants to be an expert. FWIW = nothing.
- whimsicalism 6y ago> Here you are repeating second hand criticism as if it were your own because you have not even read Go I literally just said I have read GoF, but go off. Maybe work on your reading comprehension, and also your tone.
- SoSoRoCoCo 6y agoNo, you literally said: "FWIW from what I have read of GoF" Which means your read "of" it, not "read it". > Maybe work on your reading comprehension Oh the irony. It burns.
- ncmncm 6y agoAny Design Pattern identifies a failing in the core language or its standard library. Were the language up to the job, the pattern would have been captured in a named library component, and no longer be a Pattern at all. Use of a well-designed and -tested library component is always better than re-coding the pattern again at each new use. A classical example is the hash table. In C, and similarly limited languages, you cannot write a useful hash table library. Instead, custom hash tables are re-coded over and over again in thousands of individual programs and libraries wherever they are needed. In other, similarly limited languages, the pattern is pulled into the core language, as a "dictionary" or "map" type, eliminating most need to re-code it, at the expense of flexibility in its design. In more expressive languages like C++ and Rust, hash table libraries arise, and the most commonly useful forms appear in the standard library. We have continually added to C++ and its library as we have identified patterns. New patterns arise in composing existing features and library components, so adding to them creates new opportunties for patterns to arise. As each new pattern is identified, it may be captured. Sometimes this demands new core language support for abstraction, as is seen in new Concepts and Coroutines core features. So, we naturally expect to see new library components that use the new features in commonly seen ways. The expected addition of pattern matching to core C++, similarly to what appears in Rust and certain other languages, represents a lapse: a failure to discover and specify core language features to add that would enable implementing usefully general pattern matching as a family of library components. Conflict over, e.g. simplicity vs generality, in design of the core feature is unavoidable, where, were it implementable as a library, different choices could be captured as different library components. Anytime we lapse and fail to discover core language primitives needed to implement a high-level construct, we should remember and see each use of it as a rebuke, and motivation to invent core features that would have made it redundant.
- throw_m239339 6y agoDesign patterns aren't data structure though, I agree with the rest of your statement. OO Design patterns are usually about implementing functional concepts into rigid OOP languages such as C++ or Java (though both languages have gotten better). With first class functions, there is no need for factories or strategies, or commands or a lot of stuff that imply single method classes. With pattern matching, there is no need for chains or responsibility and so on and so forth...