简体   繁体   中英

Ruby class, arguments being passed around methods, instance variable?

I'm having this problem which I think it might be a code smell, I have a class that receives an argument in its initialiser and contains one public and several private methods - everything normal. Example:

class Foo
  def initialize(a)
    @a = a
  end

  def apply?(examples)
    foo_1(examples)
  end

  private

  def foo_1(examples)
    foo_2(examples) ? 1 : 2
  end

  def foo_2(examples)
    examples.size > @a
  end
end

My problem here, is 'examples' that is received by the public method being carried around over and over the private methods, it doesn't look pretty and it seems like a code smell, what's the best approach here? Make it an instance variable inside the public method?

Thanks

Yes, this may be considered a code smell if the number of private methods accepting examples is bigger than 1-2.

One thing to consider would be to extract a class to represent the rule here.

For example:

class Foo
  def initialize(a)
    @a = a
  end

  def apply?(examples)
    size_rule_applies?(examples) ? 1 : 2
  end

  private

  def size_rule_applies?(examples)
    SizeRule.new(@a, examples).apply?
  end

  class SizeRule
    def initialize(a, examples)
      @a        = a
      @examples = examples
    end

    def apply?
      @examples.size > @a
    end
  end
end

I wouldn't make the examples an instance variable of the Foo class as there's a risk it would persist in memory between the calls to that object. I've seen bugs like that.

If examples changes dynamically then making it instance variable is not an option. You'll either need to instantiate Foo for every examples or you'll end up having mutable Foo where examples itself is changing, which is also not good.

Your code looks fine. The only thing that concerns me is that one method depends on another. It's usually not a big deal, but this would look better to me:

  def apply?(examples)
    foo_1(examples) && foo_2(examples)
  end

Another option is to use block:

class Foo
  def initialize(a)
    @a = a
  end

  def apply?(examples)
    foo_1 { examples }
  end

  private

  def foo_1
    v = foo_2 { yield.size }
    v ? 1 : 2
  end

  def foo_2
    yield > @a
  end
end

Since the class Foo does nothing else presently than just evaluate .apply?(examples) I will advice examples be added to the initializer and made an instance variable. This is simpler, more efficient and more obvious.

class Foo
  def initialize(a, examples)
   @a = a
   @examples = examples
  end

  def apply?
    foo_1
  end

  private

  def foo_1
    foo_2 ? 1 : 2
  end

  def foo_2
    @examples.size > @a
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM