5 ms·
Surprised that nobody has mentioned yet: this code is stateful for no reason. There is no need for instance variables, a constructor, an object, or a class at a
by aphyr 14y ago
Surprised that nobody has mentioned yet: this code is stateful for no reason. There is no need for instance variables, a constructor, an object, or a class at all. It's just a function. Put it in a mixin, as a module function, in a helper, as a class method; there are lots of ways to do this that don't involve extra code, nounifying verbs, and state.
- omgsean 14y agoI'm about 5-6 years into my Ruby programming career (and 5-6 years into my OO programming career) and this is something that I've been wondering about lately. When I read Design Patterns for Ruby Russ Olsen seemed to generally write classes that require instantiation, but when I look at the work of other Rubyists that I admire they're often writing classes that are nothing but class methods. I'm starting to get a pretty good feel for it, but what are the criteria that you use to determine if a class should be stateless or not?
- ori_b 14y agoThe rule of thumb I use in most cases: Can it be written statelessly without contortions or large inefficiencies? If so, then I make it stateless. Depending what is idiomatic in the language and project, I will usually put it into a function instead of a class. I find stateless code easier to reason about.
- aphyr 14y agoClasses exist to bundle state; if there's no need for state, there often shouldn't be a class in the first place. I tend to use module_fun for that, or just: module Foo def self.some_stateless_method ... end end Foo.some_stateless_method class MyController include Foo def index some_stateless_method end end and so forth. People think of modules as being mixins, but they're actually just function namespaces--which you can happen to bind into class contexts via include/extend. I tend to use class methods where: a.) The class exists, because you want state, and b.) The function operates principally on, or is only useful in, the context of that class, but c.) The function isn't bound to the object's state. That last requirement is fuzzy, since the call semantics are different: self.class.foo is awkward, so I'll often write instance methods which don't actually depend on the state, just taking advantage of the local namespace.
- tenderlove 14y agoI think it depends on the situation, and it's difficult to for me to describe. I try to think about it like this: I am writing a class to represent a car. I need to know if the cars doors are opened or closed. The `open` or `closed` attribute is the state of the car, so it makes sense that the Car object would have that state. Now compare that to the use of `LanguageDetector` from the example: LanguageDetector.new(@comment).set_language Presumably this object is keeping a reference to the comment for it's internal state. When I'm dealing with an object, I like to say in my head "language detector, can you tell me the language for some text?". Nothing in my question gave the text a 1:1 relationship with the language detector. Since my question did not identify a relationship, I would not make the comment a part of the LanguageDetector object. I'd rather write something like this: detector = LanguageDetector.new language = detector.detect_language(@comment) @comment.language = language What might the LanguageDetector actually want to have as it's state? I think maybe things like encodings, or particular languages it supports. The question I ask is "language detector, what encodings or languages do you support?", and the relationship becomes clear. For example: detector = LanguageDetector.new(:encodings => [...], :languages => [...]) # etc I hope that makes sense, and that I don't sound too crazy (talking to myself and whatnot).
- aphyr 14y agoAs a note, I think this particular variant of SRP is adopted from Java, where there is no concept of a function not bound to an object. You might be tempted to use static methods for plain-old namespaced functions, but there's a second quandary: it is really difficult to manipulate anything but instances in Java. Since there are no first-class functions, you can't very well pass these functions around to configure other objects, etc. So you wind up with layers of OOP scar tissue around the problem: DI frameworks, Handlers, Managers, Strategies, Factories, etc.
- wonderzombie 14y ago"OOP scar tissue" is my new favorite phrase. To add some real value, by this I mean that I've run into this crap a lot. In Java, sometimes I want the ability to have a function which returns other functions based on an input. This is doable in a variety of languages other than Java. In Java, I didn't call it such, but I ended up writing a FactoryFactory. (I am not proud.) I had one layer of abstraction to produce Foo objects with a certain configuration, and a layer above that to parameterize the creation of a Foo-creator. sigh. Scar tissue indeed.
- ToastOpt 14y agoYup, I've seen this as well, way too often. Rather than a function, you get a class, rather than arguments you get constructor parameters, and then the get stuck at what to name the method that actually does anything.
- Smudge 14y agoI think what you are saying is very valid. Personally, I tend to pull things out into stateful classes (e.g. CommentPoster, OrderMailer, ElephantCleaner, etc) for two reasons: 1. Testability. Isolating these bits of application logic and testing them on their own has proven less prone to issues than including it all in, say, controller methods or helpers, which get messy over time. 2. Swappability. Okay, sure, you may never need a second kind of CommentPoster, but you never know. Keeping things like this stateful and properly abstracted out saves me plenty of time as the app's requirements get more complex. It's not exactly premature optimization if I always follow this pattern and know exactly how it will work. (Sometimes it's just an unused optimization.) To me, there is very little added complexity, and if anything it makes it a bit easier in the long run.
- deleted 14y ago[deleted]
- aphyr 14y ago1. Testing is easier without state. I'm trying to illustrate that state and context (and namespaces) are separable. 2. Swapping implementations is easier: would you rather pass a function, or an object with a single function? This ties back in to testing: it's easier to create a test variant of a function than it is to create a new class. Don't get me wrong: state is great, and I use it all over the place. I generally avoid it, though, unless the state is needed, simpler to understand, or more concise.
- ajross 14y agoYes. This is "stupid OO". The author knows how to "modularize" code by segregating it into classes, and hasn't learned to view functional decomposition in the same way. Yes, justinko is doing it wrong.
- justinko 14y agohttp://cdn.memegenerator.net/instances/400x/22419205.jpg http://cdn.memegenerator.net/instances/400x/22419205.jpg