Feeding Da Brain
In 90s you would say: “I am a programmer”. Some would reply with “o.. k”. More insightful would reply with a question “which programming language?”.
21st century.. socially accepted terminology has changed a bit, now you would say “I am a developer”. Some would ask “which programming language?”. More insightful would reply with a question “which out of these 42 languages do you use the most?”
The greatest thing about using several at the same time is that feeling of constant adjustment as I jump between the languages. It feels like my brain goes through exuberant synaptogenesis over and over again building those new formations.
What's for dinner today honey?
Asynchronous brain refactoring with a gentle touch of "mental polish"
With all these new synapses, I came to love the fact that something that seemed so holy and “crystal right” before, now gets questioned and can easily be dismissed. Was it wrong all along? No. Did it change? No. So what changed then? Well.. perception did.
Inmates of the “Gang of Four” Prison
Design patterns are these “ways” of doing things that cripple new programmers, and imprison many senior ones. Instead of having an ability to think freely, we have all these “software standard patterns” which mostly have to do with limitations of “technology at time”.
Take big guys, like C++ / Java / C#, while they have many great features and ideas, these languages have horrible story of “behavior and state”: you always have to guard something. Whether it is from multiple threads, or from other people misusing it. The languages themselves promote reuse vs. decoupling: i.e. “let’s inherit that behavior”, etc..
So how do we overcome these risks and limitations? Simple: let’s create dozens of “ways” that all developers will follow to fight this together. Oh, yea, and let’s make it industry standard, call them patterns, teach them in schools, and select people by how well they can “apply” these patterns to “any” problem at hand.
Not all developers bought into this cult of course. Here is Peter Norvig’s notes from 1996, where he “dismisses” 16 out of 23 patterns from Gang of Four, by just using functions, types, modules, etc.
Builder Pattern vs. Immutable Data Structures
Builder pattern makes sense unless.. several things. There is a great “Builders vs. Option Maps” short post that talks about builder patter limitations:
* Builders are too verbose
* Builders are not data structures
* Builders are mutable
* Builders can’t easily compose
* Builders are order dependent
Due to mutable data structures (in Java/C#/alike) Builders still make sense for things like Google protobufs with simple (i.e. primitive) types, but for most cases where immutable things need to be created it is best to use immutable data structures for the above reasons.
While jumping between the languages, I often need to create things in Clojure that are implemented in Java with Builders. This is not always easy, especially when Builders rely on external state or/and when Builders need to be passed around (i.e. to achieve a certain level of composition).
Let’s say I need to create a notification client that, by design (on the Java side of things), takes some initial state (i.e. an external system connection props), and then event handlers (callbacks) are registered on it one by one, before it gets built, i.e. builds a final, immutable notification client:
SomeClassBuilder builder = SomeClass.newBuilder()
.setState( state )
.setAnotherThing( thing );
builder.register( notificationHandlerOne );
builder.register( notificationHandlerTwo );
...
builder.register( notificationHandlerN );
builder.build(); |
SomeClassBuilder builder = SomeClass.newBuilder()
.setState( state )
.setAnotherThing( thing );
builder.register( notificationHandlerOne );
builder.register( notificationHandlerTwo );
...
builder.register( notificationHandlerN );
builder.build();
In case you need to decouple “register events” logic from this monolithic piece above, you would pass that builder to the caller that would pass it down the chain. It is something that seems “normal” to do (at least to a “9 to 5” developer), since methods with side effects do not really raise any eyebrows in OO languages. In fact most of methods in those languages have side effects.
I quite often see people designing builders such as the one above (with lots of external state), and when I need to use them in Clojure, it becomes very apparent that the above is not well designed:
;; creates a "mutable" builder..
(defn- make-bldr [state thing]
(-> (SomeClass/newBuilder)
(.withState state)
(.withAnotherThing thing)))
;; wraps "builder.register(foo)" into a composable function
(defn register-event-handler! [bldr handler]
;; in case handler is just a Clojure function wrap it with something ".register" will accept
(.register bldr handler))
(defn notification-client [state thing]
(let [bldr (make-bldr state thing)]
;; ... do things that would call "register-event-handler!" passing them the "bldr"
(.build bldr))) |
;; creates a "mutable" builder..
(defn- make-bldr [state thing]
(-> (SomeClass/newBuilder)
(.withState state)
(.withAnotherThing thing)))
;; wraps "builder.register(foo)" into a composable function
(defn register-event-handler! [bldr handler]
;; in case handler is just a Clojure function wrap it with something ".register" will accept
(.register bldr handler))
(defn notification-client [state thing]
(let [bldr (make-bldr state thing)]
;; ... do things that would call "register-event-handler!" passing them the "bldr"
(.build bldr)))
Things that immediately feel “off” are: returning a mutable builder from “make-bldr”, mutating that builder in “register-event-handler!”, and returning that mutated builder back.. Not something common in Clojure at all.
Again the goal is to “decouple logic to register events from notification client creation“, if both can happen at the same time, the above Builder would work.
In Clojure it would just be a map. All data structures in Clojure are immutable, so there would be no intermediate mutable holder/builder, it would always be an immutable map. When all handlers are registered, this map would be passed to a function that would create a notification client with these handlers and start it with “state” and “thing”.
Mocking Suspicions
Another synapse formation, that was created from using many languages at the same time, convinced me that if I have to use а mock to test something, that something needs a close look, and would potentially welcome refactoring.
The most common case for mocking is:
A method of a component "A" takes a component "B" that depends on a component "C". |
A method of a component "A" takes a component "B" that depends on a component "C".
So if I want to test A’s method, I can just mock B and not to worry about C.
The flaw here is:
"B" that depends on a component "C". |
"B" that depends on a component "C".
These things are extremely beneficial to question. I used to use Spring a lot, and when I did, I loved it. Learned from it, taught it to others, and had a great sense of accomplishment when high complexity could be broken down to simple pieces and re wired together with Spring.
Time went on, and in Erlang or Clojure, or even Groovy for that matter, I used Spring less and less. I still use it for all my Java work, but not as much. So if 10 years ago:
"B" that depends on a component "C". |
"B" that depends on a component "C".
was a way of life, now, every time I see it, I ask why?. Does “B” have to depend on “C”? Can “B” be stateless and take “C” whenever it needed it, rather that be injected with it and carry that state burden?
If before “B” was:
public class B {
private final C c;
public B ( C c ) {
this.c = c;
}
public Profit doBusiness() {
return new Profit( c.doYourBusiness() + 42 );
}
} |
public class B {
private final C c;
public B ( C c ) {
this.c = c;
}
public Profit doBusiness() {
return new Profit( c.doYourBusiness() + 42 );
}
}
Can it instead just be:
public final class B {
public static Profit doBusiness( C c ) {
return new Profit( c.doYourBusiness() + 42 );
}
} |
public final class B {
public static Profit doBusiness( C c ) {
return new Profit( c.doYourBusiness() + 42 );
}
}
In most cases it can! It really can, the problem is not enough of us question that dependency, but we should.
This does not mean “B” no longer depends on “C”, it means something more: there is no “B” (“there is no spoon..”) as it just becomes a module, which does not need to be passed around as an instance. The only thing that is left from “B” is “doBusiness( C c )”. Do we need to mock “C” now? Can it, its instance disappear the way “B” did? Most likely, and even if it can’t for whatever reason (i.e. someone else’s code), I did question it, and so should you.
The more synapse formations I go through the better I learn to question pretty much everything. It is fun, and it pays back with beautiful revelations. I love my brain :)