5 ms·
CourtesyImplementation (2004)
- rowyourboat 12y agoWhy would you get disturbed by an elephant proclaiming to be exactly one elephant?
- gpvos 12y agoBeing and containing are different things. (Although I would not be disturbed by that in this cases like this.)
- deleted 12y ago[deleted]
- icebraining 12y agodef num_elephants result = 0 @children.each do |c| if c.respond_to? :num_elephants result += c.num_elephants else result += 1 end end return result end Personally, I just wouldn't mix my boxes with my elephants :)
- djur 12y agoIf the duck type is going to be checked, it should be checked when the child is added. Ensures you only call #respond_to? once per node, fails earlier. Checking types during a computation is a code smell to me.
- jrochkind1 12y agoIn fact, some OO purists from the original days of OO suggest using this technique generally to always avoid conditionals anywhere. Polymorphism over conditionals. http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism http://sourcemaking.com/refactoring/replace-conditional-with...
- mattfenwick 12y agoIt's difficult to see the point because it's not a very good example. Why do Elephants inherit from Node (they seem entirely unrelated)? How can I put things other than Elephants in boxes -- by also having them inherit from Node? Why isn't there a "Leaf" subclass of Node that contains one of anything? I think the tradeoff between adding methods to a class hierarchy and adding cases to functions is relevant here (I think this is known as the expression problem -- http://en.wikipedia.org/wiki/Expression_problem http://en.wikipedia.org/wiki/Expression_problem -- but I'm not sure), but it's not mentioned: > Now the kind_of? test in num_elephants is a smell, since we should be wary of any conditional that tests the type of an object.
- jrochkind1 12y agoSince it's ruby, there's actually NO reason for Elephant to inherit from node, and all his code would work just the same if it didn't. I think that was basically an error on his part. And, since it's ruby, the answer to "how can I put things other than Elephants in boxes", with the current code example, is "just make sure they have a #num_elephants method". Which, yeah, seems kind of insane, although part of that is the artificial example. An IMO better solution still within this kind of OO world would be to declare an interface of some kind "CountableNode" or something, and say, anything you put in a box has to implement that interface (which to begin with has one method, #num_elephants). There are ways to do that sort of thing in ruby (there are ways to do almost anything in ruby), but they aren't done very often in rubyland, which I do think is a failing in the ruby idiom. Although many rubyists will say they've never found it a problem in practice (maybe they're right, maybe they just didn't recognize the root of the problems they did have). Anyway, you make reasonable points even with these 'corrections', but Fowler could have made his example a bit tighter while still staying firmly within conventional ruby and/or OO best practices.
- djur 12y agoIdiomatic Ruby would not use inheritance in this case. 'Node' is a duck type and doesn't need to be explicitly defined. In 2004 this would have been written for an audience more conversant in C++ and Java, where you'd need to use an abstract base class and an interface respectively.
- cousin_it 12y agoThat's indeed the right pattern to use in OO. However, OO isn't necessarily the most concise approach to such problems :-) data Node = Elephant | Box [Node] count Elephant = 1 count (Box nodes) = sum (map count nodes)
- innguest 12y agoThis is many times more readable, simpler and safer than what Martin Fowler wrote. I wonder if he will ever grow out of the OO hypnosis. Maybe he just carved too comfortable a niche for himself that he needs to keep peddling it. I say this becaue I think it is harmful that he uses his fame to keep outdated programming techniques around, contributing to overtimes, bugs galore, and loss of productivity.
- djur 12y agoIt's easy to golf a more concise solution to a contrived example like that, yes. The Haskell implementation starts to get more complicated when you want to be able to define additional participants in the role of 'Node' without the original implementation having to know the name or structure of those participants. That's extremely common in game programming, for example.
- cousin_it 12y agoWell, if the additional participants only need to support the "count" operation, then you can do without tree-shaped data types at all, and just use a single Integer for the whole thing. After all, a single lazy Integer in Haskell is already an unevaluated tree of computations, there's no need to build additional trees out of it :-) Also this way you get memoization for free. If we need to support other operations as well, Haskell is perfectly capable of expressing the usual extensible OO solution, by using a record of functions. (A record type containing functions in FP is basically the same as an interface type in OO, and instances of that record type correspond to implementations of the interface.)
- djur 12y agoA record of functions permits polymorphism in behavior, but I'm not clear on how it can provide polymorphism in state or structure. I'm certainly no expert in pure FP so there may be something I'm missing, but if the operations have to produce a change in the state of an entity, each one of the functions would have to both receive and return a state, right? Or you use a record of closures, each which returns a new record of closures? It seems to me that in both cases you're discarding a lot of the benefits of Haskell's type system.