We have some comments that disparage the change, saying that it obfuscates the code and makes it more difficult to understand.
We have others saying that this change is a basic technique and it's "shocking" that the author wrote a book without this simple knowledge.
What I take from this is yet another situation of people arguing about something highly subjective that is closely tied to their identity. Good programming is not an objective concept. People respond differently to different programming techniques and what works better for one person may greatly confuse another.
A few months after I'd left I got a phone call from the guy who'd taken over the project - he rang specifically to compliment me on the code and how easy it was for him to understand what was going on and how much better that had made his work. Didn't really know how to respond as I've never had that before, but it was nice.
Not blowing my own trumpet, so to speak (although it is something I'm kinda proud of) but to provide a counter data point to your PM's
I feel ashamed when I touch it I dirty it up.
It's actually a very tricky subject. If you're working on SMe apps, including startups, most often a given project'style will flow from the lead (or the person/people who initiated the project). They may be good, they may be bad. Invariably, less experienced developers are hired and learn Ro code following this style. Eventually, those less experienced developers become experienced, and leave to persue other projects. And they take that style with them.
Depending on the role they have, there is likely still a lead above them. And this is where the arguments begin. The lead had his/her style. The new dev has experience with another style, and it may or may not mesh with the new lead's style.
And so the problem perpetuates.
It can be very difficult to work with those devs in the early-middle stage of their career. If they are good people, they learn to be flexible, adopt the best parts of all the styles they are exposed to, and eventually become great leads. But many will not, are confrontational, and ultimately poisonous to the teams they are on.
If I can quote my favorite philosopher, Ted "Theodore" Logan: "if the only true wisdom lies in knowing, then you know nothing"
Look at the writing of Joshua Bloch based on his experience with designing the Java standard libraries, you will find plenty of arguments that you could think are about "style", but in fact he always includes a very detailed and concrete discussion about why one way is superior to the other. Similarly with some of the articles of John Carmack from idSoftware. For me the revelation was that there is indeed no such thing as "style" so that some techniques are just "prettier" than the others, it is all a matter of consequences a given implementation has for other developers using the code and for its maintainers.
There are objective ways in which to judge programming, by considering whether simple rules like the SOLID principles, low coupling, DRY, using composition over inheritance, keeping code complexity measurements low, favoring immutability, statelessness and referential transparency, etc. have been followed.
If any programmer is certain of the superiority of 'his programming style' (singular), then that programmer is insufficiently humble and lacks knowledge of multiple programming styles. Tragically, if you don't know what you don't know, you can think you know everything and can thus speak with confidence on a subject.
One can wonder what comments would be left if you removed all those from programmers with less than 5 years of programming experience, programmers under 30 and programmers with experience in only a single language. My suspicion is that a pretty consistent picture would emerge, with a number of caveats and YMMV's, with an eye for the actual, intended and possible future use cases, the language, the maturity of the project, etc.
> There are objective ways in which to judge programming, by considering whether simple rules like the SOLID principles, low coupling, DRY, using composition over inheritance, keeping code complexity measurements low, favoring immutability, statelessness and referential transparency, etc. have been followed.
Hardly anyone on here would disagree that these are good programming practices (or so I think...), but the question becomes: what is the best way to favor immutability? FP solves that problem but many people don't like the all-or-nothing approach. Same with referential transparency.
> One can wonder what comments would be left if you removed all those from programmers with less than 5 years of programming experience, programmers under 30 and programmers with experience in only a single language.
Programmers under 30? Think how many languages exist that aren't even 30 years old! This is one area where I highly disagree. I have found that age matters very little with regard to someone's ability to write code. I have seen code written by "veteran programmers" that is worse than some teenager's weekend project. I'm not denying there is a correlation, but I would say it is a very weak correlation that probably isn't worth mentioning.
I do agree with programming experience, although I'll add the caveat that programming seems to be interesting compared to other subjects in that the rate at which different individuals become better at it seems to vary very dramatically.
But we all have some personal aspect of how we see the world. How we organize logic, view priorities, and such, is all personal and unique, no matter how much we may feel that it's objective.
Programming is an expression of that inner thought life. It's how you organize, categorize, and optimize, all laid out in bare raw form. It's an expression not just of our identity, but to an extent of how we exist. Being who we are, we usually tend to think highly of ourselves and our way of doing things, so it's only natural that we think highly our code style.
- the "bad" part is RowType.for(), where a type is dynamically called forth based on manipulations on some string. I agree that a more concrete mapping from string-to-type would be preferable (I'm assuming the use of a string is required for some reason).
- the "good" part of the patch is that he replaced a conditional with type dispatch. I agree that this is a basic technique of object-oriented programming.
There is a confusion about programming though, an idea that it is a purely technical discipline, like laying bricks when in reality its a design discipline, like designing a building.
If you wanted to build a house you wouldnt just hire a couple of bricklayers and tell them "do it". Yet thats what happens in programming...
There is not even a consensus on what is good in programming! Take a look at other professional cultures: people work extremely hard to develop the skills that everybody else in their profession have. What does a software developer do? Whatever they like on their own, there is no professional culture whatsoever: "Don't give a crap about the humankind's experience, I'll invent everything on my own the way I like".
This is actually unbelievable how different programming is in this aspect from other disciplines. I don't even mentor Junior Developers because of that, as I can't refer to anything authoritative and say "Do it like this" because there is always a bunch of folks that do it the opposite way. There is almost nothing to rely on.
The surprise isn't that he didn't make the choice to do it that way. The surprise is that the choice didn't occur to him. The feedback would be very different if he simply had made a different choice.
"You can reuse code with inheritance. Absolutely crazy. Brilliant.
All of this blew me away."
Honestly?
TLDR: dynamic_cast is bad design
It's a bit of a shame, because I've found code to be reliably improved by using objects as simple tables of function pointers. The same is less often true for using objects as models of things.
I agree with that a large extent - that's something that always amazes me about open-source. You're slogging along doing your thing, and then all of a sudden someone drops a huge patch on you that makes so much sense.
With that said; I did go back and re-read it, and your reading could be correct, it's hard to say. Does have a bit of humble brag to it for sure.
It is as if the OP did not grasp what OOP is all about.
For me it was epoll() that blew me away. Absolutely crazy. Brilliant. But in retrospect so damn obvious.
I also don't like how your comment tries to make fun of his efforts to learn something new.
The problem is not that he's learning, he's teaching.
The blog entry is great in that it shows how collaborating over the Internet can lead to improvement but it's indeed a bit concerning that someone who didn't know what polymorphism was had been publishing a programming book.
Then everyone is surprised when critical bugs come to light in Ruby stuff.
Here's why: When I am maintaining somebody else's code, even if I guess that this is going on, I rarely trust these kinds of name-generation tricks. When I need to change some code, I'm going to grep the codebase for 'SwitchRow' and add a new line in whatever conditional to instantiate my new class. When grep can't find any instances of SwitchRow's constructor being called, I become suspicious and it costs me more time, because I am making sure I properly understand what is actually going on.
So you're probably not a big fan of Rails, then?
The trick here I think is consistency - is name-generated type dispatch the standard across the entirety of the codebase? If it is, go wild. If it's not then I'd be much more wary of it.
Consistency and building correct expectations for future maintainers is pretty important. Doing a smart trick in one place but failing to do it in other places where it makes sense IMO tends to create a bigger mess than doing it the dumb-but-safe way everywhere.
When we are investigating application stack traces from production for a large application, it is awful when you discover that the developers decided to be clever with dynamic function names, making a large code base very difficult to support based on available logs.
If you think you're being clever you're probably just being a dick.
While the implementation itself might be a bit 'eugh' because of the meta-programming involved, there was enough thought put into code organisation to allow you to grep `RowType` to find the module that namespaces all these dynamically instantiated classes.
Hell, if the code in the tree was arranged correctly, then `tree | grep row_type` would give you the folder where they all lived.
I'd recommend the book strongly.
All of this blew me away. Like this "@mordaroso" fellow strolled on up, cocked his knee skyward, and jettisoned his leg into the rickety wooden door that previously sheltered my mind.
That was just great writing.
Edit: drafted iPad - fixed spelling. Trilby - does anyone even use that word anymore?!?
1. "Singleton" (enough said) 2. "Template method" is more or less "inheritance over delegation"
There's also a couple like Flyweight that are of such narrow applicability that they hardly merit placement on the short list with classics like Composites and Factories, and a few new items like Dependency Injection are surely worth a mention whether you want to argue for or against it.
Finally it's got an approach to presenting and explaining the patterns that clearly predates the web and modern attention spans. All that said, it's still a classic, but depending on the preferred language and technical proficiency of the reader it wouldn't always be my recommended introduction to design patterns.
Hardly.
The GoF book is sweet if in this day and age you're still doing Java/C# but if you're programming in a language like, say, Clojure you can throw about 75% of the GoF book into the trash.
pg said to use a Lisp to beat the average and that's what I'm doing. I'm certainly not accepting the status-quo and considering that, say, the "visitor pattern" is a godsend. Most of these "patterns" only make sense in a non-functional and OO language and are only needed because of missing languages concepts (like the lack of high-order function and/or the lack of real macros).
The if tree was certainly not pretty, but straightforward - it did its thing. It was something that could be given to an average developer to work on and improve.
The refactored code will require someone that's not just an average developer - if only because you need to read the article to understand it if you're not familiar with javascript, while the initial code needed only a glance to understand what it did.
IMHO, that's just complexity for complexity sake - unless you have plans to take advantage of the new flexibility it provides.
(and if you have to find, hire and pay that 'better than average' person, it might not be such a good thing.)
Adept programmer: Discovers polymorphism. It's great, uses it for everything.
Master programmer: Realizes that in many cases, it makes the code less clear and more verbose, reverts back to if/else until the flexibility is actually needed.
Same story repeats itself many times on different fronts: metaprogramming, recursion, exceptions, macros, etc.
Generically, I had something like this in my head:
switch (row_type) {
case submittable: ...; break;
case checkable: ...; break;
...
}
Then I started realizing that using a switch means you have isolated things such that they can only be one of the n types. It won't ever be submittable AND checkable, for instance. I assume that's what is desired here: mutual exclusivity for all of these possibilities.Looking back at the original code, I see that by using the nested if/else construct, it effectively gives priority to them in the order in which they are encountered. A row with a submit_button is going to fire off make_submit_cell() whether or not it has checkable set. It trumps the possible call to make_check_cell, in other words.
These two approaches solve two different problems. Without knowing more, it's hard to say whether the "priority" approach makes sense here.
If I had to guess, based on the refactoring which turned it into a series of distinct classes, that sounds like it should have been "pick 1 of n" (one-hot code) all along. If that's so, the if block wasn't just ugly, but it was actually covering up some real problems. It was just asking for trouble in terms of bugs down the road since it allowed nonsensical settings.
But that's just a guess. I could be wrong.
It looks like the purpose is a pretty common one in iOS - you have a table view populated by many rows, each of which can be a different class that renders and behaves differently. It's a common need in iOS apps.
What he really needed was an enum, not flags. The choice of class is mutually exclusive and flags are horrible for tracking this, and are great for hiding bugs - one erroneous flag flip and suddenly your code is walking down a completely different path that's non-obvious on first debug. Using a bunch of bools to approximate state is error-prone and IMO just bad architecture.
Regardless of the particular merits of the technique he talks about here, I think we can all learn a lesson from this guy about appreciating the work and ideas of others.
I know we have git and we can always do a diff to see what changed. What I am talking about is a more holistic view. Being able to search a code base for SwitchRow and then somehow the code comes up where it was refactored into this dynamic programming style.
Git can do this but only if you are actively looking for it. I wish you could do a metacomment on the refactor without cluttering up the code.
Constraining yourself to only writing code a beginner can understand is stupid. Maintaining good code and "easier to read for a beginner" code in tandem is a waste of man hours.
Clay is obviously a very talented programmer, and just like most talented programmers, he didn't start out that way. This article isn't about how he learned OOP, it's about how someone opened his eyes to how code can be elegant instead of simply just functional.
This article hits home with me especially. I just learned Python last semester in my intro to CS class. I had an eerily similar moment when I learned how to use generators. "WOW! You can simplify this huge block of code into one line! It's..beautiful." It's a really special moment, and I'm glad Clay shared his.
A lot of folks drew attention to "You can reuse code with inheritance. Absolutely crazy. Brilliant." and took as equivalent to me saying "It's absolutely crazy and brilliant you can reuse code with inheritance." I get that both in- and especially out-of-context that's how it comes off, but it's not what I meant. It was sloppy and I should've been more careful in writing.
I meant it to come off more like "...everything is dynamic and decided at run-time, plus you get a great plugin architecture if you do some polymorphic tricks with these RowType objects. And all of these benefits came from just one simple refactoring. The power of small refactors is absolutely crazy. Brilliant." (I've added this as an addendum to the post)
Like the point of the whole thing wasn't about the refactor itself and if/how the code "rocked my world", but the fact that a refactor could have a seismic effect on the future of a project and the direction it takes.
The addition of many more row "types" wasn't even in the orbit of my thinking without the refactor, and it really helped shape where the project went: there were just those 4 branches in the `if`/`else` tree in this post, but there are now 20-odd default row configurations.
Hope that helps some folks to add more context
Metaprogramming is something you do when you can't do it easily in the usual way. If a dispatch table or inheritance takes care of something, use the existing language features instead of building your own.
(EDIT: On examination, there already is a RowType, and the pull request is a lot closer to my suggestion than I first thought, so mostly ignore this.)
Now I'm feeling a bit guilty for not having open sourced any of the code I've ever written, and I'm wondering what kinds of lessons I could have learned earlier on.
What you really want is multimethods - they provide all the goodness of classic polymorphism, without forcing you to model intrincate, rigid class hierarchies/relationships.
Some pseudocode illustrating the concept:
# declaration. the 'dispatch function' we pass can build an arbitrary value out of the args it receives.
defmulti build_cell lambda { |args| args[1].tag }
# if the value the dispach function generates equals :submit, this body is gonna be called
defmethod build_cell :submit do
# implementation...
end
# more defmethods for :switch, :check, :edit etc
build_cell(row, cell)
# our lambda gets invoked, which re-passes the arguments it receives to the corresponding implementation
Multimethods doesn't seem to be have been adopted at all in the Ruby community. A quick googling reveals a couple implementations though.In the Lisp world they are first-class, even though they aren't used all the time. http://clojure.org/multimethods might be worth a read.
Nowadays I see people wanting to change Ruby to be more like those other languages. Needless to say, Ruby is best when all of Ruby is available. Putting restrictions on it wouldn't begin to turn it into what some people want.
On the server-side, people can use all kinds of languages, with no restrictions on choices. That's why these languages can flourish. On the client-side we are more restricted. The industry gets to dictate what goes on the client-side more.
Sigh.
I'm trying to use Dart lately which compiles to Javascript. While it can seem a bit like Ruby in OO and dynamic typing, it's a world of difference in other regards. Languages like Ruby that can evolve while breaking backward compatibility have more chance to be useful out of the box.
EDIT: I just checked, yes he does (well, unless we're talking about another Fabio of course, which is possible...)
Here is someone writing books and programming iOS apps who can still have his mind blown by, what seems to me, some fundamental concepts. I think there are a lot of people out there doing real work - who can benefit from a little push. that makes me feel that a) I have things I can teach and b) that its ok to not know everything - even what others may think is "basic" stuff.
The book is a good read and a fantastic reference.