Are Class Methods Evil?

Two things I like in programming: Object instances and abandoning global things.

I especially appreciate any DCI effort in the community helping us getting away from classes back to thinking about objects and clean responsibilities.

In order to explain my thoughts about DCI, here’s a first post about class methods, which seem to be a crucial problem in this newer approach.

A tweet from my good friend Michał Łomnicki recently made me think about class methods in Ruby. What he was basically saying is that class methods suck.

I’m more and more against using class methods. It often leads to procedural-like programming.

From a rant perspective I’ll second that and agree. However, class methods have a certain purpose, so let’s discuss it in this post.

What Is a Class Method?

Class methods are global methods. You may access and call them from anywhere since you don’t need an object instance.

class User
  def self.find(id)
    # ...
  end
end

In order to find a certain User instance you call the class method #find on the User class. I personally find this a handy thing.

michal = User.find(1)

As we’re creating a fresh instance we command the class to do so. And since there is no user instance around, yet, we have to use a class method rather than an instance method.

What’s the Problem With Class Methods?

Now, class methods come in handy – you don’t have to think about the context as they’re available everywhere. Consider the following misuse of a class method.

class User
  def self.email_to(id, email)
    user = User.find(id)
    Emailer.send(email, "Hello, #{user.name}")
  end

It’s obvious that #email_to has to be refactored to an instance method. Nevertheless, I see code like that quite often. Just for completeness, here’s a better version.

class User
  def email_to(email)
    Emailer.send(email, "Hello, #{name}")
  end

Object-oriented vs. Procedural

What makes the class method example being bad style is that we’re in an object-oriented environment. Maybe we can break that down to the advise “Use object instances wherever possible.”

In the example, the programmer didn’t see that the email behaviour (aka method) is tied to an instance rather than to a class. His user code will look like the following.

User.email_to(1, "michal@snatch.pl")

If subsequent method calls like this would follow, this could be considered procedural code as we’re not working on object instances but on globally available methods. And, yeah, I personally don’t like global things when it comes to programming.

Avoiding Class Methods

Right now, I can come up with two reasons for having class methods.

  • The programmer didn’t analyze his problem sufficiently, does not see the coupling to an instance and simply pushes the behaviour into a class method. That happened in the last example.
  • There simply is no object instance available at this system state and we’re urged to use a class method.

On first sight, there seems to be no way out for the latter.

Abandoning Class Methods?!

There are strong opinions on the net that class methods shouldn’t be used at all (except for the constructor, of course). I’m still unsure here.

Anyway, in a lot of cases class methods can be replaced with instance methods on factory or builder objects.

michal = user_factory.find(1)

This implies a user_factory instance is around. So my question is: Where would this come from? There must be some kind of initial class method call somewhere in the chain of actions. I’d love to see some reactions and comments on that.

Class Methods and DCI

The reason I’m writing this here is that I like the DCI approach where you dynamically extend objects at runtime according to system state.

Object instances include required behaviour and then, they die. No pollution on class layer.

This works great with instance methods but there are big problems when it comes to class method usage (see the next post). So – are class methods evil?

Tags: ,

28 Responses to “Are Class Methods Evil?”

  1. I don’t see any evil in them when appropriately used.

    The idea of having user_factory.find instead of User.find seems crazy. Stuff like User.first, File.read, Dir.glob etc is handy and useful stuff. I can’t think of any better APIs for that.

    Why would I want to do two things (instantiate an instance and then call the method) when I want to do one thing (call the method).

    The idea of a factory instance does not make sense to me especially in Ruby since the class itself is actually an instance of a Class object, and can even have its own instance variables etc if need be. So the whole thing just becomes redundant.

    Ceremony over sanity or something like that :)


  2. Dusty

    I think that class methods are like anything else. Sometimes they are evil, sometimes they are good. You should do your best to figure out when they are good and when they are evil. Try to limit the usage of evil class methods and use liberally when they are good.


  3. tom myer

    First, I haven´t any experience with DCI so I can´t share any opinions on it when used with class methods. I see your point of view when you write class methods are more procedural style than methods called on objects which I agree with. But using class methods like the ones from ActiveRecord is absolutely fine – why not?! If your oo-code results in more procedural style code I think the problem is mainly the oo-design not the usage of class methods in general.

    I wouldn´t say that class methods are global methods or functions: You forgot about the class – they are namespaced! Furthermore: There is not one class method in ruby – technically they are all instance methods.

  4. I totally agree with David Backeus : “Why would I want to do two things (instantiate an instance and then call the method) when I want to do one thing (call the method)”.

    Sure, it is a problem if you use only Class methods everywhere, instead of using them smartly and only where it make sens. But finally, I would neither say Class methods are good thing nor evil. It’s just that _in some cases_ they’re handy to get the job done.

  5. If you’re going to use a factory or a builder (or a service in the case of lookups) then you can just instantiate it.

    user_service = UserService.new
    user = user_service.find(id)

    There are a lot of reasons this is better: it’s easier to have multiple services, it’s easier to test and to mock if necessary, and I am sure there are more reasons too.


  6. metaclass

    “Class methods are global methods. You may access and call them from anywhere since you don’t need an object instance.” – no.

    class methods are an instance methods of eigen class. you can access instance of eigen class… only by some reference, e.g. by constant called “::User”.

    want clean programming? stop using constants

  7. I tend to follow these rules: A method that acts on a single object should be an instance method. A method that acts on a group of similar objects should be a class method.

  8. Hi Nick!

    That’s great we have some discussion about that.

    Naturally it’s not possible to get rid of class methods but we can try to reduce it and use objects instead of classes.

    Stateless nature of class methods reminds me procedural programming in some way. I’ve blogged about that http://is.gd/Y2HPoX

    Also Andrzej showed some neat concepts for ActiveRecord http://is.gd/9cHEbz

    @Anthony Eden – right, testing is yet another important argument.


  9. Mulasse

    Like every problem, the solution is not “black or white”.

    Not using static method is not better than use it inappropriately as in your example.

    Math class is the perfect example of using static methods.
    D’you really think it’s a good way to do:

    m = Math.new
    m.sin(Math::PI)

    I prefer:

    Math.sin(Math::PI)

    Obviously, your example is voluntary stupid, but the problem is not that static exists, it’s bad programmers that use it this way.

    Java drops operator overload because “it’s obfuscating”. It’s not. Some programmers just use it like asses. That’s the same for static methods.

    If you drop static methods, then welcome to Java and its billions of useless factories.


  10. Mulasse

    PS: Drop static methods like “find” in your example means being able to access an instance of factory everywhere.

    If you really wanna do this, drop ruby / rails and switch to Spring with his delicious IOC and its tons of xml configuration.

  11. The discussion you started here has been around the last ten years in Java and let to IoC/DI with many many annotations, xml files and so forth. More modern Java Frameworks like playframework are now back to Class Methods because they are simpler.

    So there is not Black/White answer, like most of the times it depends. The proposed solution of this article doesn’t provide any advantages over the current situation, so why change it?

  12. I like to have class methods which serve as handy aliases for one or a few related method calls and with some simple logic inside. This doesn’t prevent me from actually having other classes implementing this logic if it becomes a huge chunk (e.g., Foo.find may instantiate a Search object).
    If you need to call other class methods in one class method, then you likely have a case from which you can factor-out a module/class.

  13. I agree class methods are handy and simpler to user than instance methods. Although in long run objects are easier to extend and maintain. Well maybe that’s just a matter of taste.

    In my opinion Math.sin is like Converter.to_megabytes(5).
    We prefer 5.megabytes as it’s way cleaner and more readable, right? I’ve updated the post with some deeper explanation of utility class methods
    http://is.gd/bPwSy7


  14. David Loeffler

    This argument has been around for at least 30 years. I implemented a simple version of Flavors in MacLisp on Multics in ’82. Flavors was written by Howard Cannon at MIT. Howard got inspiration from Alan Kay’s Smalltalk. Later, when I was on the ANSI committee for Common Lisp, CLOS was defined.

    The beauty of Ruby, like Lisp and Smalltalk, is its openness. Programmers have much more freedom and can be more expressive. I built very large systems in Java since ’97 and have decided to switch back to a dynamic language. I could go on and on but I’ll just end this with paraphrase of a quote I heard from Kristen Nygaard at the ’86 OOPSLA: Ruby lets me work, Java makes me work. (Kristen was inventor of Simula and his comment had Ruby -> Mac, Java -> PC. )


  15. Bartosz Blimke

    @Anthony Eden – is testing class methods still an issue in Ruby. I can’t find reasons why testing or mocking class methods in Ruby would be easier than instance methods on service objects. could you give an example?

  16. Your example just demonstrates the lack of the programmer to use an instance method instead of a class method. This does not even hint that a class method could be “evil”. How is ease of use “evil”. user_factory would just be an instance of whatever class you create, only used once. Multiple user_factory instances would never need to be created, so why would it be a class, a reusable bit of code?

  17. Bartosz:

    Here’s an example of why objects may be better for testing purposes. It describes it more at the architecture level, but I think it still applies to this discussion:

    http://alistair.cockburn.us/Hexagonal+architecture

    I think it’s easier to use mocks with objects than with classes.


  18. Bartosz Blimke

    Andrzej:

    In Ruby, I don’t see any difference in difficulty of mocking class methods or instance methods.

    Following Anthonys view, I’m curious about cases, where testing instance methods of a service object would be easier than testing a class method on i.e. User class. In ruby of course.
    Delegating responsibilities from one class to separate objects is of course desirable and produces cleaner code. Tests are smaller and cleaner, but that’s really an advantage of delegation and principle of a single responsibility per class, rather than an advantage of instance methods.
    Not sure how this article answers my question. Could you be more specific?


  19. franee

    if i follow correctly from reasoning, your better example should be:

    class User
    def email_to(email)_obj
    emailer = Emailer.new
    emailer.send(email, “Hello, #{name}”)
    end
    end

    Which I find weird. There are still uses for class methods, which I think should really depend on the context/use.


  20. Agostino Deligia

    The way I see it, a Ruby class *is* the factory for its instances. That is:
    User.find(1) is the same thing (semantically) as user_factory.find(1). The difference is
    that I don’t have to instantiate the factory. Really, class methods are nothing more than
    instance methods on the class object.

  21. I definitely agree with your general principles. I definitely like instance#extend. i definitely agree class methods should be avoided where possible.

    But I’m not sure they’re evil, and I’m not sure they should _always_ be avoided — following that can lead to horribly over-complex architecture, a sort of premature abstraction. Your mention of ‘factories’ gives me nightmares about Java-style over/premature abstraction where you need to interact with (and therefore understand) WAY too many different classes to get anything done.

    Your ActiveRecord example is a useful one, because — has anything ill ever come of ActiveRecord’s class methods? Has anyone ever (like, seriously, EVER), run into a case where they couldn’t conveniently do what they wanted because of ActiveRecord’s class methods? It would perhaps have to be tinkering with the internals of AR itself or doing something else very abstract, and even then I’m not sure it’s ever happened.

    I think maybe some of the disadvantages of class methods are possibly lessened in a non-statically-typed duck-type-happy language like ruby. After all, when you’re calling User.find, it doesn’t really matter WHAT User is, so long as it’s got a #find method that takes the expected params and returns the right results. I can’t quite connect the dots, but I feel like this ameliorates some of the problems with class methods.

    But perhaps the happy medium is coming with clear patterns that let you start out with class methods but easily refactor to something else if it becomes neccesary, rather than ‘prematurely’ abstract right away.

    As an aside, I can’t be sure, but I feel like my attraction to the design principles you’re talking about here (which I think are really just ‘real’ OO, not something different than OO), resulting in lots of objects and lots of use of instance#extend, may be giving me performance problems in ruby/rails, bah.


  22. Nathan Zook

    Ruby was my reintroduction to OO after an eight year recovery from clashing with C++. I view MyClass.new as a convenience method for myobj = Object.new ; myobj.extend MyClassAsAModule. So I agree strongly that instances of class Class should primarily be factory objects.

    Confusion arises, however, in that classes are also (almost) modules. For instance, in the above comment, a commenter referred to Math as a class. It is not a class, it is a module. And modules themselves have dual roles. First, they are repositories of collections of methods to be injected into objects. (Class#extend injects the module into the instances of the (Class) object being extended.)

    Secondly, they are global objects collecting methods. Again, as mentioned above, the convenience (even clarity) in the case of Modules is almost certainly enough to allow us to overlook the ugliness of the fact that Math.sin is really a global method that we have to work to get to.

    The issue seems to be that Class objects include this Module behavior. They don’t just create instances, their (public) methods can do ANYTHING. I remember discussions with my mention regarding what a Class object really should be doing. Clearly, a Class object is a factory. Clearly, class variables in ruby are EVIL. Not so clear (at first), is that using a Class object as a double for a collection of its instances is a BAD THING.

    The moral: Class < Module is a LIE.

  23. When I’m programming in C# on the .NET Fx not using class methods is important, primarily for ease of mocking. I enjoy programming in Ruby because static vs. instance methods at runtime isn’t that much of a problem, or at least not that much of a problem in Ruby.

    What is important here is that in a dynamic language like Ruby you don’t have to spend so much time worrying about ceremony, you can get right down to business and produce a maintainable solution quickly. I’m not advocating discarding decades of good programming practice but I am saying don’t over-engineer to make your elegant Ruby solution look more like an architect astronaut working with Java. Its just not worth it.

    (Moral: static methods aren’t the devil.)

  24. Classes are factories.

  25. Says (user) metaclass: “want clean programming? stop using constants”

    Well, in Ruby a class name is just a constant. So not using class names would mean to stop using class names, too. … And anyway, “obj = Class.new” is valid Ruby…

    Not everything you can do, is something you should do.


  26. Brad

    There is a pretty big difference between the classes in languages like Ruby vs languages like Java/C#.

    Many of the problems around static methods in Java are because of the problems with testability and refactoring, because the Class itself is fundamentally NOT an object, so you can’t easily / mock the call (yes – I know some advanced mocking libs now can)

    This doesn’t happen in languages where the class itself is an object. The Ruby class method isn’t equivalent to the Java static, even if you might write similar methods with them.

    Personally, for a small app I would happily do User.find(1), but on the larger apps I work in such logic is usually through defined services, and often differing representations based on access levels, so you might see something like

    tenant = FindTenants.new(current_user).findByProperty(property)

  27. [...] I here people saying “class methods are evil” (here and here‚ for example). But I think for my use case‚ a class method is exactly the [...]

Leave a Reply