← Back to Upcase

Are class methods an antipattern?


(George Ulmer) #1

I haven’t read this anywhere, but talking it over with a friend makes me think that class methods are an antipattern, or at least belong is a separate class from instance methods.

They tend to operate on multiple records, or create records. This makes their functionality very different from an instance method who’s knowledge is restricted to itself.

Also, in a project I am working on, they tend to have the following pattern:

  1. Find a record by one (or two) fields.
  2. Check a property.
  3. Return another value based on that property.

This is very different from an instance method that updates it’s own properties.

It’s hard to explain why they feel like an antipattern, so I’d love to hear other people’s thoughts and ideas.

Also, I didn’t find a good category for this, so perhaps there could be a category like “Code Design” or “Patterns/Anti-patterns”


(Ben Orenstein) #2

I like this article’s take on the subject: http://blog.codeclimate.com/blog/2012/11/14/why-ruby-class-methods-resist-refactoring/


(Joel Quenneville) #3

Class methods per se are not an anti-pattern but they are often overused/abused.

How I use class methods

“Factory” methods

In general, I tend to use class methods in a factory-esque role where they return an instance of the class. For example:

Game.read_from_save_file(my_save_file) # => instance of Game

If the behavior starts to grow complex, I would extract it into a GameSaveReader class that might be used like this:

reader = GameSaveReader.new(my_save_file) # => GameSaveReader instance
reader.extract_game # => instance of Game

I might still keep Game.read_from_save_file and just change its implementation to delegate to GameSaveReader.

Aliases

Sometimes, creating an instance and then calling a method on it can be a little cumbersome. In that case I will create a class method that will delegate to an instance. Going back to the example above:

class GameSaveReader
  def self.extract_game_from(save_file)
    new(save_file).extract_game
  end
end

Note that this is just a convenience method. All the heavy lifting is done by the instance.

ActiveRecord

ActiveRecord gives you two main class method types: those that return an instance of the model (such as find) and those that return a collection of instances (such as where). Most of the behavior that you will write with class methods here will be collection-related while the single object behavior would be implemented at the instance level.

A classic example of user-written class methods for ActiveRecord models would be scopes. These return a collection of instances that match a certain criteria.

ActiveRecord is a little strange because it handles both the collection of objects at the class-level and single objects at the instance level. Typically you would have separate collection object whose instance methods would handle collection-level behavior (such as scopes). Most front-end frameworks (Backbone, Ember, and friends) are built this way. However, ActiveRecord is what it is and I’m not recommending that you re-write all your models to break up collection behavior into it’s own class.

Anti-patterns

Multiple responsibilities

Class methods are prone to feature envy. If you have a lot of class methods in your model, take a moment to think: Is the class taking over a responsibility that should belong to the instance? Or perhaps this behavior deserves its own class?

Non-stateful methods are all defined on the class

Just because a method does not touch state does not mean that it should be a class method. Does anyone but the instance use this method? If not, it almost certainly should be a private instance method.

Conclusion

The main responsibility of class methods is to create instances. Because they are global in nature and resist refactoring, you typically want the business logic to be implemented at the instance level (this is object-oriented programming after all).

So are class methods evil? No, they have a vital (but limited) role to play in the implementation of your project.