Best Practices for Working With Methods

How Ruby methods actually work and best practices for implementing them.

Scroll down...

Content

Resources

Comments

You've been using methods since square one and writing your own as well so we'll focus on the slightly more advanced stuff in this lesson. We'll cover what methods should do and some stylistic issues that come up frequently.

A Method Should do ONE Thing

Each method should only do ONE thing. If it's doing two, it's time for another method. If it's doing a dozen, you probably need to start thinking about having a separate class.

What Goes In a Method?

What should you put into methods? Everything! All your program logic should be encapsulated in methods.

Methods help organize your code by keeping you from repeating yourself so anything that you find yourself doing repetitively should also go in its own method.

Why are Methods Useful?

Methods make your code much easier to read and understand. You'll appreciate this the first time you have to debug a bit of horrendously complicated spaghetti code. It can take you longer to figure out what the code is actually doing in the first place than to fix the error.

Let's say you're building a game that uses an until loop to keep taking turns until the game is over when someone wins. But figuring out if someone has won takes several lines of code. You really can't pack 20 lines of logic into the condition for your until loop. How do you fix this?

It's better to write a method called game_over? that does all the work. Then you can simply and legibly write until game_over? and it's obvious what your code does. Even if the game_over? logic is a single line, breaking it into an appropriately named method makes your code more legible.

For instance, the game_over? method probably needs to check if the human player has won or has lost, so maybe it just contains calls to two other methods, victorious? and defeated?. That's okay!

If you were debugging, it'd be very easy for you to go and find where the problem is if the player just can't seem to lose:

def game_over?
  victorious? || defeated?
end
def victorious?
  # check for victory, return true/false
end
def defeated?
  # check for loss, return true/false
end

Methods Should Be Short

The brevity in the method above is a good thing!

If your methods are >10 lines, you're probably doing too much. When you look at the open-source projects on Github, their methods are often incredibly short (the comments take up all the space). Good code doesn't look long, it looks brief but descriptive.

What Should a Method Modify?

A method should be self-contained (encapsulated) and only use those variables that have been passed in. Don't modify global variables or otherwise have side effects for your methods unless absolutely necessary.

By the same token, don't destructively modify the arguments or the object your method was called on (unless it's explicitly a bang! method).

Naming Methods

When naming methods the goal is to be descriptive but short. Name based on what it will return or what the major intended side effect will be. The name, while short, should still be fully descriptive because the method should only do one thing anyway.

Avoid names like get_xyz because just naming the method xyz is sufficient.

If you can't tell what the method will return based on the name, you probably need a better name. If your method name seems insanely long, your method may be trying to do more than one thing. End with a question mark ? if it will return true/false.

Always use snake_case.

# BAD: avoid get_
def get_health(player)
  player.health
end

# GOOD  
def health(player)
  player.health
end

# BAD: needs ?
def is_healthy(player)  # Need a ?
  player.health > 100
end

# GOOD
def healthy?(player)
  player.health > 100
end

# BAD: Vague name, does too many things
def get_attacked(player, damage)
  player.health -= damage
  if player.health < 0
    player.dead = true
  end
end

# GOOD
def take_damage(player, damage)
  player.health -= damage
end

What is self?

self is a word that you see a whole lot in Ruby and it's actually pretty simple... it refers to whatever object is running the current method (the "caller"). So if I called current_user.jump, current_user is the caller of that method. Inside the definition of the jump method, self would refer to the current_user.

For another example:

class String
  def who_called
    puts self
  end
end
"I did!".who_called
"I did!"
#=> nil

That is incredibly useful because we create methods that could be called by any number of different objects so we need a way inside of that method to dynamically refer to whatever object called it.

When you start working in Rails, you might want to return the full name of a user and self will help you out:

def full_name
  "#{self.first_name} #{self.last_name}"
end

# Console
> User.first.full_name
#=> "Foo Bar"

Another Way of Running a Method

Sometimes you want to dynamically insert a method that should be called so you don't know its name ahead of time. That's one of the cool parts of Ruby!

To do this, use send. It's simple to use -- just call it on the original object and pass in any additional parameters as normal. Adapted from the docs:

class Klass
  def hello(string)
    "Hello #{string}"
  end
end

> k = Klass.new
#=> #<Klass:0x007ff25b9132c8> 
> k.send :hello, "gentle readers"   
#=> "Hello gentle readers"

This is not often seen in beginning Ruby but it comes up a fair bit when you get more advanced.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

There are no additional resources for this lesson just yet!

Sorry, comments aren't active just yet!

Next Lesson: Other Ruby Tidbits