← Back to Upcase

When to use private and protected?


(Justin Gordon) #1

Any style guidelines on when to use private and protected in a large Rails application?

I find that I rarely use these keywords.

Also, I maybe wrong, but the use of Concerns with private/protected did not seem to be as simple as in a regular class.


(Ben Orenstein) #2

Check out the Learn app’s source code. You’ll see we use private constantly.

Almost all my classes have private methods. I use that to keep my public API small while still breaking up my classes into many small methods.


(Geoff Harcourt) #3

Agreed with @benorenstein on making frequent and aggressive use of private. I don’t find occasion to use protected. I’m sure a computer science purist could point out places where I should be using protected methods, but I seem to be served fine between just public and private.


(Sean Griffin) #4

Basically every method should start out as private, and then be promoted to public as needed. As for protected, it’s significantly less useful in Ruby than other languages, because private isn’t actually “private”.

In most languages, private would restrict access outside of where it’s defined. The notable difference between this and what Ruby does is subclasses. Usually protected means it’s not part of the public API of an object, but is part of the API available to subclasses.

In Ruby, private means that the method cannot be called with a subject (ironically including self), so you must use the implicit self to call the method. Protected allows you to call the method with a target, but only from within the same class. The only time I’ve ever found that this is useful is for methods used in comparison. So for example:

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

  private

  attr_reader :items
end

However, if I wanted to add an == method, it’d probably look like this:

def ==(other)
  items == other.items
end

However, items now needs to be protected for me to call it from inside of ==


(Justin Gordon) #5

Hi Sean. Why would you have a private attr_reader on :items rather than simply referring to @items throughout the class?

Also, what is the the meaning of private within a Rails concern module?


(Sean Griffin) #6

Hey Justin,

Sorry for the delayed response. As a rule of thumb, I never access ivars outside of constructors, getters, and setters. By following this rule, it becomes trivial to move a local variable to an instance variable, or move a property from one object to another (adding a delegator), without having to change unrelated code. For example, if my code looked like this:

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

  def fooable_items
    @items.filter(&:fooable?)
  end
end

and I wanted to refactor it to this:

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

  def fooable_items
    bar.items.filter(&:fooable?)
  end
end

class Bar
  def initialize(items)
    @items = items
  end

  attr_reader :items
end

My fooable_items method had to change as a result of me changing where @items lives. However, if I’d written it like this:

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

  def fooable_items
    items.select(&:fooable?)
  end

  private

  attr_reader :items
end

then once I move items to Bar, I only have to change the attr_reader, instead of all of my code.

class Foo
  extend Forwardable

  def initialize(bar)
    @bar = bar
  end

  def fooable_items
    items.select(&:fooable?)
  end

  private

  attr_reader :bar 
  delegate items: :bar 
end

(Sean Griffin) #7

Also, the meaning of private in a module is the same as in anywhere else. It can’t be called on a target, so it must be called with the implicit form of self. That means that private methods in a module will still be visible to classes that mix in the module.