The equivalent would be a pattern language that app users themselves can use for their apps. Instead of having the app developers drive design with A/B testing to gain user traction (with a societal risk of dark patterns), the users themselves put things together that would work with how they are doing things.
The implementation of the code would work from there. Something the end-users can change the code themselves.
Smalltalk was designed to allow that level of user participation ... but the legacy we ended up with is OOP and gang of four.
If anything, something like TCL/TK is closer to that vision than what we currently have. (What we currently have: mass lockin of consumers and end-users by a different Gang of Four — Facebook, Amazon, Apple, Google).
Alexander’s keynote to the ‘96 OOPSLA conference goes into detail about his ideas and how it can be applicable to software engineering. He was imploring software engineers not to make the same mistake that architects did with his pattern language ideas ... but we still made the same mistake anyways.
http://journal.stuffwithstuff.com/2010/09/18/futureproofing-...
Factories, for example. They're essentially just constructors. So why not use the new keyword? Turns out new has limitations:
- Dependency on the constructed type at the ABI level
- Cannot return subtypes of the constructed type
- Special keyword, not orthogonal with the rest of the language
Singletons, for example. They're essentially classes with static methods and state. Why not use these language features instead of coming up with incredibly convoluted ways to ensure only a single instance of an object has been created? Turns out classes have limitations: - Classes aren't objects
- Classes cannot be passed as parameters
- Classes cannot implement interfaces
The need for all of these patterns simply disappears in a better designed language. In Ruby for example everyone uses factory methods without even realizing it and classes are fully featured objects that can be used in any context. ThingThereCanBeOnlyOneOf a;
ThingThereCanBeOnlyOneOf b;
Oops. That's not a singleton. That depends on nobody, ever, anywhere in the code creating another one. In contrast, a true singleton can enforce that there is only one, no matter what the callers do.At least with a singleton you can create a bunch of "standard configurations" by calling them with a different constructor at startup time. So you can have a test DB during tests, different log targets, read from different filesystem (not the real one, but the one in the testdata/ directory) ...
Better yet is to have a "context" abstraction that you pass down the chain. Issue with that is, every method needs to have a first parameter that is the context ... so it requires some discipline.
Some frameworks (java @#$@#%@# servlet spec for example) make this parameter implicit and then use something akin to exception resolution to get this parameter (so when you do GetContext(), it unwinds the stack to give you the correct context). That makes things very complicated, and is an invisible LOT of state that gets passed down the stack. Still better than singletons or static methods, but good luck figuring out why a context is set to X.
In all cases you need to be very careful to USE the context. DO NOT use database except the one in the context, no caches except the one in the context, no filesystem reading, logging, ... except by redirecting through the context.
It's a state that cannot be naturally duplicated, alright, a "single source of truth". But lazy instantiation is nothing more than a very important and very common second. The pattern itself lies in preventing public-level instantiation (i.e. in Java it could translate to a class with a private constructor, private instantiation and access to the instance through a class-level/static getter, and also making it a final class).
That said, a global singleton (and global state in general) is usually an anti-pattern; having pretty much any code be able to depend on the state makes it very difficult to evaluate the impact of a change.
Right. Except in Smalltalk they are
Edit: Also, depending on the circumstances, you can probably just pass the class or its constructor instead of passing a newly written function. That would get you down to ~0 LOC involved in replicating the functionality of this pattern.
Another pattern called the Visitor Pattern is similarly a shim from dynamic dispatch on one argument to dynamic dispatch on two arguments. You just really hope you don't need dynamic dispatch on five arguments one day.
Agree. Came across a blogpost that describes this very well by using Clojure as an example: http://mishadoff.com/blog/clojure-design-patterns/
Or the inverse, the patterns are common enough that a limited form of them is baked directly into the language itself.
It's 25 years old, but still interesting.
Can you imagine?
"You can talk about patterns as a means of description, but don't consciously wield them as a means of production. Also, do not make statements such as 'the architect used this pattern'. Rather say, 'the pattern spontaneously emerged in the work, facilitated by the architect's intuition'."
Haha, right.
The second best explanation is that patterns are often a sign of shortcomings in the language. OP gives the example of iterators. Another example is the visitor pattern. Or async/yield as a replacement for explicit state machines.
A thing I'd really like to see in a language would be language level support for the command pattern (being able to capture arbitrary blocks of code, lift the execution to an object, and undo/redo it). Or to be able to include the state of an object in the type system - not like Rust does it with typestates, but to be able to say `Child` is a `Person` where `age < 18`, and if the compiler can't prove that the condition always holds, then it will force you to write the precondition checks or check it at runtime.