本帖最后由 jieforest 于 2012-5-28 11:48 编辑
We can derive a lot of good object oriented practices from this idea. Two that are relevant here:
1.Single responsibility principle (SRP): To be able to prove that an object works correctly in isolation, that object's behaviour has to be clearly defined. The more responsibilities an object has, the more complex its behaviour becomes, and is therefore more difficult to prove and reason about.
For example, if we put all of our business logic in ActiveRecord::Base subclasses, the burden of proving that our code is correct becomes immense because we expose ourselves to mistaken interactions with all of the behaviour we've inherited. ActiveRecord::Base already has a responsibility: persistence.
2.Dependency injection (DI): Many objects have dependencies — other objects that they collaborate with to accomplish their goals. If those dependencies aren't configurable in some way, then we can't test the behaviour of an object without implicitly testing its collaborators as well. By injecting our dependencies, we can easily replace them with alternative implementations. In our tests, we can inject fake objects in order to isolate the object in question.
In Search of Something Better
So, how should we organize our applications? There are a few approaches floating around.
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 isencapsulated in lots of very simple objects known as “services” and “policies”. Not allobjects in this methodology will fit in to one of those two categories, but they are twoof the most important concepts.
Service objects are responsible for retrieving and/or manipulating data — essentially any work that needs to be done that you might have put in a controller or model object before. They typically only have one method, which I like to define as “#call” (they're usually named something like PictureCreationService, so naming the method #create would seem redundant).
Policy objects are responsible for enforcing access control policies. We use them in before filters to guard controller actions, reuse them in views to conditionally display pieces of UI (example: a delete button that requires administrative privileges), and anywhere else policies need enforcing, like background jobs. Policy objects only ever have one public method: “#allowed?”. |