Objectify: A Better Way to Build Rails Applications

[复制链接]
查看11 | 回复1 | 2014-2-19 11:55:14 | 显示全部楼层 |阅读模式
本帖最后由 jieforest 于 2012-5-28 11:48 编辑
For almost 6 years, the dominant"best practice" for building rails applications has beenskinny controller, fat model. In other words, put all ofyour business logic in to your models — keeping it out of your controllers. Theresult is typically a small number of bloated objects that are impossible toreason about or test in isolation [1].
That property is important. To understandwhy, let's take a quick and highly selective look at the origins of objectoriented programming.
OnEncapsulation
One of the early papers that emphasizedthe importance of encapsulation in software development was James H. MorrisJr.'s Protection inProgramming Languages. He argued that if we were going to beable to write correct software, “programs” (probably most analogous to objectsin the OOP world) needed “protection” from each other.
...hostilityis not a necessary precondition for catastrophic interference between programs.An undebugged program, coexisting with other programs, might as well beregarded as having been written by a malicious enemy—even if all the programshave the same author!
Weoffer the following as a desideratum for a programming system: A programmershould be able to prove that his programs have various properties and do notmalfunction, solely on the basis of what he can see from his private balliwick.The idea is that if we can prove thatcomponents work in isolation, then we have a better chance of having afunctioning system when we assemble them in to a larger whole.
Aside from being central to the thesis ina paper that heavily influenced the development of object oriented programmingitself, it doesn't seem like a stretch to argue that components that areprovably correct in isolation would make a good building block from which tobuild working systems [2].

回复

使用道具 举报

千问 | 2014-2-19 11:55:14 | 显示全部楼层
本帖最后由 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?”.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则