Applying composition over inheritance to controllers is one of those approaches with which you just can't go wrong.
The idea is to have as thin of a controller as possible. The controller should just define the request/response processes, but anything substantial happening in between is going to be defined outside of the class.
For instance, if you had a controller that filtered a collection of
Product instances by a certain criteria, apply the VAT to the base price and then produce a tabular representation, CSV or JSON response, you'd end up with the following classes:
- A class that takes the request object and returns an appropriate collection of
Product instances; this class is concerned with knowing how to construct an appropriate query based on the incoming request (like querying products based on a mix of attribute values).
- A class that takes a
Product collection, processes each entry and returns the resulting collection; this class is concerned with taking a product's specified VAT and applying it on the base price.
- A class that takes the incoming request object and a
Product collection and produces an appropriate response; this class is concerned with figuring out whether to produce an HTML, CSV or JSON response of the collection.
- Your actual controller that simply creates a network of communication between the three classes; this class finally specifies the notion of collecting, processing and displaying the
Product model appropriately.
The thing is that controllers often end up exhibiting a lot of complex behavior, but this way that behavior is factored into several different components that are defined and tested independently. By simply mixing in different classes and changing a few lines of code, your controller's behavior changes dramatically. I don't really have any experience with Ruby, but from what I know this can be achieved by including and mixing different modules and perhaps even a macro or two for seasoning.
One good example of this approach are Django's generic controllers. They're just types composed of different mixins where each mixin defines certain behavior and exposes class attributes and instance methods that can be specified/overriden to configure their behavior. The generic controllers provided are just a particular combination of existing mixins that are suited for a particular problem.
Another good example is Android with it's controllers. An
Acitivty is called by the instrumentation framework whenever the activity needs to respond to a certain kind of request (create yourself, start yourself, destroy yourself) with certain input, but it's all happening on a UI thread that expects these methods to be snappy. Anything substantial, such as number crunching, retrieving data over HTTP, collecting data from the DB, so forth, is actually managed by objects of some external types that communicate some data back to the calling controller. Sometimes, this communication is generic enough that it can structured by implementing interfaces and the behavior pertaining to each is specified in an anonymous class. Again, you end up with a simple controller that only wires a network of communication between different objects to define some complex behavior, but the behavior is defined in a compact form from which you can readily figure out what's going on.
This is the code reuse promised by the composition principle: if your controller's behavior needs to be migrated to a different controller or if a certain kind of behavior needs to be shared between multiple controllers, composition over inheritance provides you with the ability to do so. And what's best, by applying Liskov substitution principle, the possibilities become endless!