Created
June 3, 2013 21:50
-
-
Save JEG2/5701747 to your computer and use it in GitHub Desktop.
Revisions
-
JEG2 created this gist
Jun 3, 2013 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,56 @@ # How Should We Use Struct? ## The Choice It's common in Ruby to see some code setup a `Struct` like this: ```ruby class Specialized < Struct.new(:whatever) # ... define custom methods here... end ``` `Struct` supports this kind of modification using a block though, so the above could be written as: ```ruby Specialized = Struct.new(:whatever) do # ... define custom methods here... end ``` Those are the possibilities. ## My Opinion There's no right or wrong way to handle this. However, I do prefer the second form. I'm going to try to make a case for why that is. I have two main reasons for favoring the second form. First, I find the second style conceptually easier to clarify. To me, the first form is kind of heavyweight, concept-wise. You need to understand that `Struct.new()` returns a `Class` and that the parent class in a class definition can be an arbitrary Ruby expression. I think it gets even tougher to understand when you eventually run into the problem of combining this style with Ruby's open classes or some autoloading mechanism. When someone comes to me and asks, "Why am I getting a parent class mismatch error?" Explaining that involves more twists. Well, you see, you didn't just customize a `Struct`. You built a `Struct` and then defined a subclass of that. Because that `Struct` is dynamically constructed, you get a different parent class anytime Ruby evaluates that expression, and Ruby doesn't allow the same subclass to reference different parent classes. Yuck. I don't even like describing this complexity and it's sort of reflected in the code by the anonymous `Class`: => [MyStruct, #<Class:0x007f8ba7389d18>, Struct, Enumerable, Object, PP::ObjectMixin, Kernel, BasicObject] In contrast, I feel like describing the second form is about as simple as "… and the constructor can take a block for customizing what is created, say by defining new methods in it." The second thing I prefer about the block form is a concept I've become aware of thanks to Josh Susser: code malleability. Consider that you begin with a trivial `Struct`: ```ruby Trivial = Struct.new(:whatever) ``` Now, if I later need to customize it, I can either just tack a block onto the end of the call or begin restructuring the statement. Then what if I decide to back out the customizations? Again, I can restructure or just remove the block. In other words, the block form flows naturally into and out of the normal `Struct` usage. It's more malleable. This means refactoring is easier and you are more likely do it when needed. There's another kind of related point here. Normal `Struct` usage has a similar form to block `Struct` usage. This visual similarity gives me the hint, "Oh, this is just a `Struct` that I am dealing with." Again, these are just my opinions.