Hacker News new | ask | show | jobs
by stiff 5139 days ago
This is not object-oriented at all, unless you learned about object-orientation from Java and its monster frameworks, this is basically a form of data-flow programming. OO is about message passing, here the "objects" do not pass any messages at all, the supposed "objects" _are_ the messages, and the routing of those messages takes place in the configuration in routes.rb, so the objects do almost no message-passing between themselves at all! It is ironic to see this advertised as "better OOP practices for rails apps".

Having said this, I respect the attempt, the problem it tries to solve certainly does exist to some extent, and the solution cannot be completely dismissed very easily.

But please, please, do learn both A) basic history and B) basic theory of the field you are working in. If you try to reinvent everything you decrease your chances of contributing something new manyfold.

1 comments

I've read at least a dozen papers on the history of OO, not to mention books and papers on current practices. This is what I came up with. I made what I think is a pretty coherent argument for why in the article. I'd love to hear your refutation of the points I made in there.
You have to think a bit more about what OO really is about. How would properties of your solution differ if you instead of classes split all the code into many very small global methods? The essence of OO is objects freely passing messages between themselves, if you fix all the message routing and put it in a central place you are not doing OO anymore, you loose the most fundamental properties, for example:

http://en.wikipedia.org/wiki/Dynamic_dispatch

You also don't have encapsulation anymore, since to be able to perform the majority of the work in the services, the services have to operate directly on data owned by other objects.

How can you combine the policies in ways other than a simple logical AND? How do you combine services?

Others have also already pointed out other issues.

Please don't be offended by the above. Software engineering is unfortunately a field where some valuable content is buried underneath loads of BS, since it is so easy to "philosophize" about it even with just basic programming experience and this leads other people learning the field to easy misconceptions. I do not want to discredit the work you put into this and it might be that in some form something valuable will come from some of the ideas. It is an interesting problem to work on and for me it was an interesting solution to think about, I just don't think you are there yet.

> You have to think a bit more about what OO really is about.

If you want to have a respectful debate, I'm completely game. If you'd prefer to continue with your incredibly condescending tone, then I'll bow out here.

Well, maybe I got carried away a bit with the tone in general in the last paragraphs of both comments, but I think I substantiated the particular sentence you cited quite a bit. On the other hand, I also took half an hour to clearly explain to you why this is somewhat misguided and tried hard to be polite, while someone below just said "this is fucking ridiculous". It's hard, at least for me, to give constructive criticism, without the other person being offeneded.
I find utilising the Feedback Sandwich Technique, with some authentic positive encouragement, usually works well.

I apply it mostly when I'm writing something and am getting the feeling that it might not come across so well.

http://www.rightattitudes.com/2008/02/20/sandwich-feedback-t... (from a quick Google)

You need to grow thicker skin, that first sentence was in no way condescending and gives you no reason to just ignore the rest of his comment.
No. It may be unintentional, but the tone of stiff's comments is highly condescending.

Exhibit A: Whatever you think about James's choices in this library, he has clearly thought a lot about OOP. It's just silly to start a comment with "You have to think a bit more about what OO really is about."

>You need to grow thicker skin

I'm beginning to be convinced this is almost never an appropriate thing to say.

I think what the GP might be trying to get at is this: you've reinvented partial function application and first-class functions. An instance of a class can be regarded as a bundle of functions which all have the constructor's parameters partially applied. If your instance only has one method, it's equivalent to a single, partially applied function. In that sense, what you've done is found a way to reorganise a Rails app in a very functional way - and, if you want to do that, given that Ruby doesn't have first-class functions or partial application (well, without proc hack, anyway), this may be the best way to do it.

It may work very well, but it's at least as functional as it is OO.

From the examples it doesn't even look like a _partial_ function application since it is not clear whether you can control how those services/policies/responders are initialized. Also I think it would be quite awkward for one service to initialize another and call it, so it is not really functional programming either, since there are no direct function calls. Since all the flow of the data is described in the routes file, it isn't OO at all and mostly reassembles dataflow programming as I said:

http://en.wikipedia.org/wiki/Dataflow_programming

Parts of this approach might be valuable, as I tried to emphasize, but as a whole this doesn't look good for plenty of reasons mentioned in the thread.

The CurrentUserResolver example in the README shows that some initialisation control is possible here.

> it is not really functional programming either, since there are no direct function calls.

There don't have to be. The classes and instances are being referred to by the developer in the same way as any first-class functions defined elsewhere would be - they're actually resolved and called by the framework.

> Since all the flow of the data is described in the routes file, it isn't OO at all and mostly reassembles dataflow programming as I said

It certainly is OO. There are instances, and they receive and act on messages. Yes, there's a dataflow-like DSL for routing, but that's not to the exclusion of other paradigms.

The CurrentUserResolver example in the README shows that some initialisation control is possible here.

This is the dependency injection part, not the policy/service/responder core of the framework I am talking about.

There don't have to be. The classes and instances are being referred to by the developer in the same way as any first-class functions defined elsewhere would be - they're actually resolved and called by the framework.

I am not talking about what happens on the level of Ruby, I am talking about what happens logically at the level of the framework. The classes represent functions, sure, but they do not call other functions (services/policies/responders), the only "function-calling" is done by the router. Yes, this too is a form of functional programming, the form known as dataflow programming where the flow of the data is described explicitly.

It certainly is OO. There are instances, and they receive and act on messages. Yes, there's a dataflow-like DSL for routing, but that's not to the exclusion of other paradigms.

It isn't OO at the level of the framework. Yes, Ruby is underneath, there are classes, there are instances, and the thin ORM layer is object oriented, but the objects within the framework do not exchange messages, the only object doing all the message dispatch is the router. This is not OO, just look at the traits listed here: http://en.wikipedia.org/wiki/Object-oriented_programming

The framework basically forces you to abandon messaging (since there is no exchange of messages, just single directional messages flow from the router), abandon encapsulation (since the services have to access the data of the domain objects and hence the domain objects must make their data available to everyone) and abandon polymorphism (since the services are statically dispatched by the router). How can this be object oriented?

> The framework basically forces you to abandon messaging (since there is no exchange of messages, just single directional messages flow from the router),

The services receive messages from the router, then send messages to whatever they need to call to get the job done. Being sent messages by the framework is what makes it a framework and not a library; I don't get your objection here.

> abandon encapsulation (since the services have to access the data of the domain objects and hence the domain objects must make their data available to everyone)

Only if you're conflating domain objects with persistence objects.

> and abandon polymorphism (since the services are statically dispatched by the router).

They're statically dispatched on a constant name, not on a class. If you need polymorphism at that point, it's trivial to define a dynamic service resolver - all you need is something with a #new method assigned to the constant to do whatever late binding you want.