Just to add a couple of points... Struts implements many design
patterns, but one of the more important ones is the command pattern. Actions are Command objectes as described by this patter, and as has been mentioned, they are very much part of the Control. The "controller" is the ActionServlet, although it delegates much of its responsibilities to the RequestProcessor. So with the exception of ActionForms and the taglibs and probably a few others, the vast majority of the Struts package falls squarely into the realm of the control.
But what about ActionForms? This seems to confuse people sometimes as to where they fit within the whole MVC thing. Although some people think of them as part of the control or model, ActionForms are part of the View. Validation tends to muddy this a bit, as people often (mistakenly?) perform business validation on ActionForms, but as they are so tightly coupled to the view components, the only conclusion to be drawn is that they are part of the view.
Getting back to the Actions, the trap many people fall into with these is an issue of tier leakage. That is, business logic all too often finds its way into the Struts Actions. A Struts Action should really be pretty concise and not have all that many lines of code in it. If people get into the habit of using Business Delegates, they can generally avoid the problem of tier leakage to a greater extent. Each Business Delegate method likely solves a use case, making it so that you can almost slap on an entirely new controller and framework to your application with relative ease.
Assume our application is a forum application such as JavaRanch. Our user wants to view a particular
thread from a list of threads. A typical architecture might go something like this....
1. User selects link to a thread he wishes to view and submits. Thread id is submitted in the request.
2. ActionServlet gets a hold of the request and hands it off to the RequestProcessor who will decide what to do with it.
3. The RequestProcessor sees that the user wants to view a thread, determines that the correct command to delegate to is the ViewCommandAction, and hands off the request to that Command.
4. The Action grabs the thread id. It gets a hold of a ForumThreadDelegate and passes the thread id to its getThread() method, along with maybe some user information.
5. The getThread method of ForumThreadDelegate gets a hold of a Data Access Object (DAO) which for lack of a better term I'll call ThreadDAO. The Delegate in turn calls the ThreadDAO.getThread() method passing the thread ID.
6. The ThreadDAO.getThread() method accesses the database and constructs a Thread object from the data in the database that corresponds to the requested thread, if it exists. This Thread object is returned
7. Now the Delegate inspects the returned Thread and compares it to the user information that was passed in to determine whether or not the user has permission to view the requested thread. Assuming the user is allowed to view the thread, the Delegate returns the Thread object back to the Action.
8. The Action now has a Thread object. The Action doesn't want to give this entire object to the view however, and instead needs to assemble an ActionForm containing data from the Thread thatthe view needs. It uses an Assembler object to do this, which we will call ThreadAssembler. The ThreadAssembler has a method called assembleForm taking an ActionForm destination and a Thread source as arguments.
9. The ThreadAssembler.assembleForm() method takes the pertinent data frm the model object, Thread, and populates the ActionForm with it. The ThreadAssembler also has another method, assembleThread() which goes the other direction, populating a Thread object based on the contents of an ActionForm, but we're not using that method right now.
10. The Action now has an ActionForm in the proper state to be used by the view, so it can now use the ActionMapping to find the correct ActionForward and return it to the RequestProcessor.
11. The user will now be forwarded to a view of the thread he has selected.
Our Action populated an ActionForm because I made the assumption that for whatever reason the thread is presented to the user within the context of an HTML form that he or she might input data into. A little hoky to be sure, but it illustrates all the points I wanted to. If a form was not displayed in the view, I could have either populated some kind of a Thread DTO, or if it was appropriate, return the Thread object itself.
So this kind of illustrates how all the parts of the MVC work together with each part doing only the work it should be doing. It helps to further break things down into various tiers such as the View Tier, Control or Application Tier, Business Tier, and Persistence Tier. Your JSPs and ActionForms fall into the view tier, all the rest of the Struts stuff generally falls into the control tier, your business delgates, model, and other business objects fall into the business tier, and your DAOs are part of the persistence tier. If you think in terms of these tiers or layers, you may find it easier to avoid tier leakage and maintain a more sound design.
[ March 20, 2005: Message edited by: Jason Menard ]