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.
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.
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."
I'm beginning to be convinced this is almost never an appropriate thing to say.
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)
It may work very well, but it's at least as functional as it is OO.
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.
> 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.
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 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.
If it was a framework modelled in an object oriented way it would itself be built of classes exchanging messages, but it isn't. It encourages you to also structure your code in a non object oriented way by creating hundreds of little services which are basically procedure calls instead of making your model accept a wider variety of messages or instead of building classes with more than just a "call" method. Most of the code is supposed to go directly into the services, that's the whole point of the framework, remember it is a solution to the "fat model" problem:
One in particular is something I've been thinking about and refining for a while now [3]. In this approach, persistence objects remain extremely thin, and business logic is encapsulated in lots of very simple objects known as “services” and “policies”.
Also, keep in mind that the services do not normally call each other (they could in theory, but that would be even more awkward), so instead of having message exchange between models you have duplicated code in services or have to use inheritance. Ruby is object oriented so sure there are message calls further down, it doesn't make the framework object oriented on the logical level.
Only if you're conflating domain objects with persistence objects.
Must you go as far as to refer to a hypothetical reality just to win an argument ;) ? That's how Rails works, that's what the examples show, I am not referring to what is possible in theory, but to what the framework does and the most reasonable possible way of using it. Honestly one more level of indirection would make this look completely ridiculous.
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.
Ehm, no? You cannot just define a "new" method returning whatever you want neither in a module nor in a class in Ruby. You would have to overwrite the initialization methods that are usually hidden from the user. Passing a constant name is almost equivalent to passing a class.
module Foo
def new
5
end
end
puts Foo.new.inspect
# undefined method `new' for Foo:Module (NoMethodError)
class Bar
def new
5
end
end
puts Bar.new.inspect
# #<Bar:0xa146324>Eh? The sentence you quoted explains that putting domain logic in the persistence objects is precisely what objectify avoids. I do't see any examples of domain logic as separate from persistence logic at all in the examples, so I'm not sure where you're getting the idea that the framework encourages it from. If you're that tied to "how Rails works", why are we even discussing this? Objectify exists to change the way Rails works, because the way Rails works is broken.
> Ehm, no?
Ehm, yes.
class MyFunkyResolver
def new
5
end
end
Foo = MyFunkyResolver.new
p Foo.new
# 5I don't understand what you are trying to say. As far as I remember we are discussing here whether Objectify breaks encapsulation. In plain Rails you would have business logic together with persistence in a single class, and the data would be available to the external world only to the extent the business logic allows it. With Objectify all the data in the persistence objects has to be public for the services to be able to access it, and all the business logic is in many small services with only a single "call" method. Do you consider this to be good encapsulation? I am not tied to how Rails works, but I cannot judge all the possible ways of using Objectify, I can only judge what was presented/encouraged in examples and in the blog post and assume that you will use standard Rails practices in the rest of places, since it's in the end a Rails plugin and not a separate framework.
You are right regarding polymorphism.