1

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

2
  • Query objects violate “Tell Don't Ask” and the return type indicates you're not using “Duck Typing”. I suggest rethinking the implementation in terms of these for pure OO to help stay out of trouble in the future. Commented Dec 4, 2017 at 0:20
  • 1
    There's way too little information about the actual requirements and no real-world example use case for this. Normally lack of thereof might be fine for a constructive discussion, but since this is all about good design patterns more information is really required. Otherwise adding examples to initializers and instantiating the class for every query might be a reasonable solution. Commented Dec 4, 2017 at 8:03

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Since one is creating a new instance of a SizeRule class for every query anyway, why not add examples to Foo's initializer?
You can have one instance of Foo with the a always set, but then use this object several times to ask for apply? with different examples every time. At least, that's how I understand this setup here.
0

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

Comments

0

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.