

The Secret To Rails OO Design - ddagradi
http://blog.steveklabnik.com/2011/09/06/the-secret-to-rails-oo-design.html

======
tptacek
I don't know, Steve, it looks like you're creating classes with long scary
names just to "encapsulate" Ruby one-liners; your whole DictionaryPresenter
class (for instance) boils down to:

    
    
      Post.all.inject(Hash.new {|h, k| h[k] = []}) 
        {|hash, post| hash[post.name[0]] = post; hash }
    

You might be right that the forest of classes you're creating will be useful
in the long run. But this is exactly the slippery slope that the Gang of Four
disciples fell down in Java.

I think using the phrase "Plain Old $X Object" might be a warning sign.

Any time you write a post talking about learning OO, and your example class
hierarchy includes classes that do nothing but wrap things that are already
idiomatic in the language, expect your friends and those who care to jump on
you to find and squash the radioactive object bees that are trying to burrow
their way into your spinal column.

~~~
mechanical_fish
It's true: There is nothing less fun than chasing injected dependencies around
from place to place in the code, only to discover that they all boil down to
one line that you could have just... _read_.

Skimming through this essay, it looked as if the second half might be more
convincing than the first, because there the logic being refactored was
significantly hairier than one line.

~~~
tptacek
The second half of this post made me think about _Style: Toward Clarity And
Grace_, the (geekiest ever) writing book Richard Gabriel recommends (and it is
great). The first fifth of the book does little else but rail against
"nominalization", which is the bad writing habit of turning verbs into nouns,
which abets flabby passive voice writing.

Isn't that exactly what Steve's doing here?

 _Combining the flexibility of Ruby with one of my favorite patterns from
"Working Effectively with Legacy Code," we can take complex computations and
turn them into objects._

AAAGH! BEES!

Look at Norvig code. I'm struck by how he just _gets to the point_. "Here's
how I'm modeling my data. Here's a few functions that work with the model:
whack, whack, whack! Here's 'main'. Boom, I solved Soduku."

"TurnaroundCalculator" doesn't even need state. Why is it a class with a bunch
of private methods? Sure, this code doesn't belong in an AR model. So stick it
in a function!

~~~
mechanical_fish
Yeah, I'm convinced. The passive-voice metaphor alone is difficult to resist.

------
dasil003
What's not mentioned here is how you decide _not_ to extract something.

I think it's true that there is a class of novice programmers who will always
just use whatever frameworks they are given, and write things out inline,
never searching for a better way because they just don't have the natural
curiosity (or they've never been shown the light) to seek out the better ways.

It's also true that to master a programming language you have to practice
using all its abstractions. Ruby has a very pleasing and simple way to break
down concerns, and clearly this is a tool you need in your box to write great
Ruby code.

The true level of mastery is when you understand the cost-benefit of all this.
For instance, breaking everything down into one-line methods is only of
benefit if those methods are a non-leaky abstraction and make conceptual sense
on their own. It's not inherently true that a 10-line method is harder to
read—one ten-line method is much shorter than 10 one-line methods.

The effect is even more powerful with classes. Every class file you add is
adding to the overhead of understanding the program. In order for that to be
beneficial, you have a greater-than-linear improvement in program power and
flexibility. Even if some extraction seems reusable, it might require tweaks
every time you attempt to make a new use of it, or it might be a leaky
abstraction that introduces bugs that cost you time down the line. What makes
the true master are not principles about how long methods and classes should
be, but rather how to factor the logic in such a way as to maximize re-use and
conceptual integrity.

Finally, you must always be cognizant of the YAGNI principle. In a Rails app,
you are writing code for a discrete problem domain where reusability may be
limited. Taking the time to factor things truly beautifully is more worth it
at the Gem/Framework level than the application level because there is much
greater opportunity for reuse.

------
gnufied
To those who are attacking this article. I think, Steve unfortunately has
chosen a somewhat bad example to illustrate a very good point.

I have seen countless examples of complex payment logic shoved down into User
model (or CreditCard model), because developer did not had the foresight to
see that Payment logic does not belong in User or in CreditCard model.

About functional or using higher order functions - yeah go for it. The great
thing about Ruby I think is - you can still use best of OO patterns with
minimum amount of heavy lifting and sprinkle blocks and lambdas - resulting in
pretty neat design overall.

Also, functional code != good code sometimes
([http://www.drmaciver.com/2008/08/functional-code-not-
equal-g...](http://www.drmaciver.com/2008/08/functional-code-not-equal-good-
code/))

~~~
tptacek
Steve isn't talking about which model code belongs to. Nobody is attacking the
idea of factoring code out of models. But I'm attacking the idea that the
place for any refactored code to go is into its own class.

~~~
gnufied
But I am not talking about which model code belongs to either. I sm saying,
the Payment logic probably does not belong in either of the models (User or
CreditCard) and should be in its own class and should have an well defined
interface - independent of how User is stored or how CreditCard is stored. And
I think, thats exactly what steve is talking about.

~~~
tptacek
I think there may be object bees on you. Just because "logic" doesn't belong
in a model class doesn't mean it needs _its own class_. In Rails, you already
have three good places to move code like this to:

* The controller class, to generate a populated instance variable used by a view

* The helper class, to transform a general-purpose instance variable into the variable you need in your view

* As a free function or static method anywhere in lib/, particularly if you could conceivably re-use the code in another project.

The issue here is nominalizing all the logic in your code; besides creating
unnecessary indirection (indirection is the coin of the realm; spend it
wisely), it also creates One More Thing everyone has to remember to use your
code: how to instantiate your object.

Just write functions. People have built and maintained things far more amazing
than blog software in C using nothing but well-named functions. What Would
Norvig Do?

------
equalarrow
Helpers.

Enjoyed the post, but Rails also provides helpers to 'help' you with view
specific logic. Granted, this might not be as fun as a plain Ruby class, but
I've used helpers extensively on various projects and whenever I join an
existing project, it's one of the first places I look with dealing with the
view layer.

One of the nice Rails conventions.

~~~
alttab
Perfect response. My thoughts exactly. The only thing I would add is if its
reusuable, document the method! If this is part of the apps "framework",
document it! I can't count how many times I've run into basic app helpers with
undocumented option hashes. Want your code to be reused? Make using it easier
than rewriting it.

------
xentronium
This code he calls good:

    
    
        DictionaryPresenter.new(PostCategorizationPolicy, Post.all).as_dictionary
    

Well, it smells like typical java overengineering. Why stop at that, let's go
full way to:

    
    
        DictionaryPresenterFactory.new(PostCategorizationPolicyStrategy, Post.collect(CollectionStrategy::All)).present

------
benaston
In this blog post, the author is reifying concepts and improving separation of
concerns(1). This is basic software development. Which makes me wonder why the
OP got the impression this kind of thing was "secret"?

(1)The usual warnings about premature optimization apply.

------
LeafStorm
One thing that people seem to forget about object orientation: when you're not
actually using an object to represent state, but you still want to inherit
and/or separate functionality, there is a simple way to do it while avoiding
the creation of a bunch of junk objects that do nothing besides waste heap
space and get garbage collected on the next cycle:

STATIC/CLASS METHODS.

------
adelevie
A minor tangent, but do db tables whose models use `include` instead of
inheriting a base class count as POROs?

------
zoidberg_md
from an organizational pov, where do these poros go in the structure. Would
you put them in the model folder ?

------
bascule
I always liked the term PORC (Plain Old Ruby Classes) for the Ruby analogue of
a POJO

~~~
tptacek
Doesn't it worry you that you're working in an environment that dedicates so
much time to filing code away into objects that it needs a name for the idea
of an object that doesn't derive from one of the framework classes?

An antidote to this mentality:

<http://norvig.com/design-patterns/>

