Saturday, December 16, 2006

Ruby: Use class methods to reduce duplication

Context: I had two method definitions in my class that were similar to the code below.
class TaxRemoteFacade

def state_tax
...
@remote.close
end

def federal_tax
...
@remote.close
end

end
I've only shown two, but we actually needed the @remote.close code at the end of several methods.

Solution: To reduce the duplication you could introduce a mixin with a method to remove the duplication.
module RemoteFacade
def remote_call(method_name, &block)
class_eval do
define_method name do
instance_eval &block
@remote.close
end
end
end
end
The TaxRemoteFacade can now be defined as the code below.
class TaxRemoteFacade
extend RemoteFacade

remote_call do :state_tax do
...
end

remote_call do :federal_tax do
...
end

end

8 comments:

  1. Neat trick - is there any reason why you'd choose a superclass over a mixin?

    ReplyDelete
  2. In my particular instance we already had the superclass so it seemed like it made sense. However, I do prefer mixins. Good catch.

    ReplyDelete
  3. Anonymous6:24 PM

    Oooh, a little slick ad hoc AOP, eh?

    Verra nice.

    ReplyDelete
  4. I'd like to suggest an alternative

    module Remote
    def self.included(base)
    base.extend(ClassMethods)
    end
    module ClassMethods
    def remote_methods(*names)
    names.each do |name|
    define_method("#{name}_with_remote") do |*args|
    begin
    send("#{name}_without_remote", *args)
    ensure
    @remote.close
    end
    end
    alias_method_chain(name, :remote)
    end
    end
    end
    end

    class TaxFacade
    def state_tax
    ...
    end
    def federal_tax
    ...
    end
    end

    # Somewhere else...
    class TaxFacade
    include Remote
    remote_methods :state_tax, :federal_tax
    end

    Contrary to what Dan writes, the original approach is not AOP at all as it intermingles concerns. That's what I've tried to avoid with my alternative.

    ReplyDelete
  5. Good OOP solution! It's nice to see someone applying OOP patterns instead of "how Ruby crazy can I get"? For the record, you've just implemented the Template Pattern. It's one of my favorites.

    ReplyDelete
  6. @Seth: I'm not convinced my suggestion really is an instance of the Template Method pattern. There's a similarity, of course, but compared to Template Method, mixing in + aliasing put things upside down.

    With Template Method, you have a base class that defines an algorithmic skeleton, and subclasses that fill in the concrete steps. The base class comes before its subclasses.

    In my suggestion, the TaxFacade comes first. Then another (singleton) class containing templates is snug in as its superclass and TaxFacades methods are redefined to fit in the templates.

    Let's call it Inverse Template Mixin pattern. In AOP terms, it is after advice applied to an execution pointcut.

    ReplyDelete
  7. Anonymous2:41 AM

    the day comes and you realize that one or more of those methods needs to return some other value. then all your fancy metacode is for shit. unless you have a real Aspect don't bother.

    btw check out:
    http://groups.google.com/group/ruby-talk-google/browse_thread/thread/a42156ab1c955941/5d908fb73dd885dc?lnk=gst&q=trans+aop+bending&rnum=1&hl=en#5d908fb73dd885dc

    ReplyDelete
  8. The idea on this post wasn't meant to address Aspects in Ruby. In fact the only responsibility of the class is to call the tax remote facade. And, the idea of the post was how to remove some duplication.

    I was hesitant to bring this up since I do want to encourage other implementations and fresh ideas, but I think the comments have gotten a bit off topic.

    ReplyDelete

Note: Only a member of this blog may post a comment.