Design Patterns web based applications [closed]
Asked Answered
P

5

367

I am designing a simple web-based application. I am new to this web-based domain.I needed your advice regarding the design patterns like how responsibility should be distributed among Servlets, criteria to make new Servlet, etc.

Actually, I have few entities on my home page and corresponding to each one of them we have few options like add, edit and delete. Earlier I was using one Servlet per options like Servlet1 for add entity1, Servlet2 for edit entity1 and so on and in this way we ended up having a large number of servlets.

Now we are changing our design. My question is how you exactly choose how you choose the responsibility of a servlet. Should we have one Servlet per entity which will process all it's options and forward request to the service layer. Or should we have one servlet for the whole page which will process the whole page request and then forward it to the corresponding service layer? Also, should the request object forwarded to service layer or not.

Pedometer answered 22/8, 2010 at 10:20 Comment(1)
Not really official design patterns, but don't forget PRG (post-redirect-get) and Hijax (make work with no js first, then hijack the links and buttons with ajax)Pilose
P
501

A bit decent web application consists of a mix of design patterns. I'll mention only the most important ones.


Model View Controller pattern

The core (architectural) design pattern you'd like to use is the Model-View-Controller pattern. The Controller is to be represented by a Servlet which (in)directly creates/uses a specific Model and View based on the request. The Model is to be represented by Javabean classes. This is often further dividable in Business Model which contains the actions (behaviour) and Data Model which contains the data (information). The View is to be represented by JSP files which have direct access to the (Data) Model by EL (Expression Language).

Then, there are variations based on how actions and events are handled. The popular ones are:

  • Request (action) based MVC: this is the simplest to implement. The (Business) Model works directly with HttpServletRequest and HttpServletResponse objects. You have to gather, convert and validate the request parameters (mostly) yourself. The View can be represented by plain vanilla HTML/CSS/JS and it does not maintain state across requests. This is how among others Spring MVC, Struts and Stripes works.

  • Component based MVC: this is harder to implement. But you end up with a simpler model and view wherein all the "raw" Servlet API is abstracted completely away. You shouldn't have the need to gather, convert and validate the request parameters yourself. The Controller does this task and sets the gathered, converted and validated request parameters in the Model. All you need to do is to define action methods which works directly with the model properties. The View is represented by "components" in flavor of JSP taglibs or XML elements which in turn generates HTML/CSS/JS. The state of the View for the subsequent requests is maintained in the session. This is particularly helpful for server-side conversion, validation and value change events. This is how among others JSF, Wicket and Play! works.

As a side note, hobbying around with a homegrown MVC framework is a very nice learning exercise, and I do recommend it as long as you keep it for personal/private purposes. But once you go professional, then it's strongly recommended to pick an existing framework rather than reinventing your own. Learning an existing and well-developed framework takes in long term less time than developing and maintaining a robust framework yourself.

In the below detailed explanation I'll restrict myself to request based MVC since that's easier to implement.


Front Controller pattern (Mediator pattern)

First, the Controller part should implement the Front Controller pattern (which is a specialized kind of Mediator pattern). It should consist of only a single servlet which provides a centralized entry point of all requests. It should create the Model based on information available by the request, such as the pathinfo or servletpath, the method and/or specific parameters. The Business Model is called Action in the below HttpServlet example.

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        Action action = ActionFactory.getAction(request);
        String view = action.execute(request, response);

        if (view.equals(request.getPathInfo().substring(1)) {
            request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
        }
        else {
            response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
        }
    }
    catch (Exception e) {
        throw new ServletException("Executing action failed.", e);
    }
}

Executing the action should return some identifier to locate the view. Simplest would be to use it as filename of the JSP. Map this servlet on a specific url-pattern in web.xml, e.g. /pages/*, *.do or even just *.html.

In case of prefix-patterns as for example /pages/* you could then invoke URL's like http://example.com/pages/register, http://example.com/pages/login, etc and provide /WEB-INF/register.jsp, /WEB-INF/login.jsp with the appropriate GET and POST actions. The parts register, login, etc are then available by request.getPathInfo() as in above example.

When you're using suffix-patterns like *.do, *.html, etc, then you could then invoke URL's like http://example.com/register.do, http://example.com/login.do, etc and you should change the code examples in this answer (also the ActionFactory) to extract the register and login parts by request.getServletPath() instead.


Strategy pattern

The Action should follow the Strategy pattern. It needs to be defined as an abstract/interface type which should do the work based on the passed-in arguments of the abstract method (this is the difference with the Command pattern, wherein the abstract/interface type should do the work based on the arguments which are been passed-in during the creation of the implementation).

public interface Action {
    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

You may want to make the Exception more specific with a custom exception like ActionException. It's just a basic kickoff example, the rest is all up to you.

Here's an example of a LoginAction which (as its name says) logs in the user. The User itself is in turn a Data Model. The View is aware of the presence of the User.

public class LoginAction implements Action {

    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userDAO.find(username, password);

        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            return "home"; // Redirect to home page.
        }
        else {
            request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
            return "login"; // Go back to redisplay login form with error.
        }
    }

}

Factory method pattern

The ActionFactory should follow the Factory method pattern. Basically, it should provide a creational method which returns a concrete implementation of an abstract/interface type. In this case, it should return an implementation of the Action interface based on the information provided by the request. For example, the method and pathinfo (the pathinfo is the part after the context and servlet path in the request URL, excluding the query string).

public static Action getAction(HttpServletRequest request) {
    return actions.get(request.getMethod() + request.getPathInfo());
}

The actions in turn should be some static/applicationwide Map<String, Action> which holds all known actions. It's up to you how to fill this map. Hardcoding:

actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...

Or configurable based on a properties/XML configuration file in the classpath: (pseudo)

for (Entry entry : configuration) {
    actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}

Or dynamically based on a scan in the classpath for classes implementing a certain interface and/or annotation: (pseudo)

for (ClassFile classFile : classpath) {
    if (classFile.isInstanceOf(Action.class)) {
       actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
    }
}

Keep in mind to create a "do nothing" Action for the case there's no mapping. Let it for example return directly the request.getPathInfo().substring(1) then.


Other patterns

Those were the important patterns so far.

To get a step further, you could use the Facade pattern to create a Context class which in turn wraps the request and response objects and offers several convenience methods delegating to the request and response objects and pass that as argument into the Action#execute() method instead. This adds an extra abstract layer to hide the raw Servlet API away. You should then basically end up with zero import javax.servlet.* declarations in every Action implementation. In JSF terms, this is what the FacesContext and ExternalContext classes are doing. You can find a concrete example in this answer.

Then there's the State pattern for the case that you'd like to add an extra abstraction layer to split the tasks of gathering the request parameters, converting them, validating them, updating the model values and execute the actions. In JSF terms, this is what the LifeCycle is doing.

Then there's the Composite pattern for the case that you'd like to create a component based view which can be attached with the model and whose behaviour depends on the state of the request based lifecycle. In JSF terms, this is what the UIComponent represent.

This way you can evolve bit by bit towards a component based framework.


See also:

Puerile answered 22/8, 2010 at 16:33 Comment(23)
In abstract factory pattern section you described, you are using Map to store key/value pair. This map stores all the partial URL and the action servlet that is invoked. But at what point, should I fill in this map? If I can put all the values in the map at deployment time, it sounds efficient. OR can I specify the key/value in web.xml (i.e context-param) since it's application wide variable?Ottoman
@masato: You could do this in for example a static initializer block.Puerile
@masato: by the way, if you'd like to retrieve them from web.xml, then you could use a ServletContextListener for this. Have the factory implement it (and register as <listener> in web.xml) and do the filling job during contextInitialized() method.Puerile
@BalusC: Front controller pattern approach works when the actions.execute(request, response) returns string like "register" as you mentioned. But what if the first servlet needs to delegate the task to another servlet? For instance, first servlet is "register_servlet" and this servlet forwards to another servlet "post_register" instead of returning view? In such case, the post_register servlet needs to extends httpservlet and also implement Action?Ottoman
I just tested the case I described above and it works if post_register servlet extends httpServlet and do all work in doPost() which requestDispatcher eventually forward to, say, complete_register.jsp but the my point is, with my approach, initial execute() will skip the rest of code and just ignore the front controller pattern flow isn't it? I'm thinking how to handle this the best way.Ottoman
Do the job which the "post_servlet" should do in the action instead. You shouldn't have more than one servlet. Business stuff should be done in action classes. If you'd like it to be a new request, then return to a different view which would cause a redirect and do the job in the new action associated with the GET request.Puerile
Can anyone please explain what is the PRG pattern in the code above?Vassily
@Basil: en.wikipedia.org/wiki/Post/Redirect/GetPuerile
How do(if) JavaBeans fit into all this?Cheekpiece
@Jonny: it's the model. Model classes are usually javabeans. See also stackoverflow.com/questions/1727603/…Puerile
@BalusC, thanks. I have another question if you don't mind? Where should I put validation for say something like a user registration form? I am using this design, with a service layer linking commands & dao's.Cheekpiece
Depends. Easiest is to just do it right in the Action implementation the same way as with normal servlets (see also servlets wiki for a basic example, which you're free to refactor further into some Validator interface). But you could also do it before invoking the action, but this is more complex as it requires the validation rules being known on a per-view basis. JSF has covered this by offering required="true", validator="customValidatorName", etc in the XHTML markup.Puerile
@Puerile , I have a command called CheckUsername to perform server-side validation which returns a HashMap with error messages like the one in the wiki example, but what if I want to perform client-side validation with Ajax and jQuery? Can I use the same command or should I create a new one called CheckUsernameAjax that sets and returns a HashMap as json even though this is duplicating code? p.s. I am not allowed use JSF for this project.Cheekpiece
I think it will be easier to learn patterns from existing application than googling those examples. Could you provide a link to application that follows those patterns (including those at the end and other best practices that you mention in your answers)?Snowbound
@BalusC: Do you think there is anything wrong in using both getServletPath() and getPathInfo() to determine the action to match with the url? ie. String methodAndPath = method + ( (servletPath==null)? "" : servletPath ) + ( (pathInfo==null) ? "" : pathInfo ); and then do the matching accordingly.Maxa
@AndreyBotalov: check source code of MVC frameworks like JSF, Spring MVC, Wicket, Struts2, etc. They are all open source.Puerile
@AdrienBe: Depends on the URL pattern. Pathinfo is always null on suffix patterns like *.do and servletpath is always the entire URL pattern (without wildcard) on prefix patterns like /pages/*.Puerile
@BalusC: I'm only saying that because I had Pathinfo being null in some cases using pattern like /pages/* . Combining both the Pathinfo and the Servletpath made my life easier... but I'm just unsure how neat that is.Maxa
@AdrienBe: apparently you've mapped it on multiple URL patterns and the matching one was a suffix pattern.Puerile
@BalusC: something like that.. lol. It got me very confused. I guess I'll have to play around more to find out. I'll post this probably "not so good" design on my blog to see what people say. Will let you when so you can have a look if you're interested.Maxa
Your factory method pattern looks to me like a concrete (or simple) factory. The name is kind of misleading, but the GoF factory method is a polymorphic call (on an abstract class or interface type) that returns a fixed type depending on the concrete implementation of the method. Very different from a concrete/simple factory.Bratislava
@Fuhrmanator: you just described the abstract factory which is indeed different from factory method.Puerile
Nope, not unless there is more than one hierarchy. https://mcmap.net/q/15456/-what-are-the-differences-between-abstract-factory-and-factory-design-patternsBratislava
A
13

In the beaten-up MVC pattern, the Servlet is "C" - controller.

Its main job is to do initial request evaluation and then dispatch the processing based on the initial evaluation to the specific worker. One of the worker's responsibilities may be to setup some presentation layer beans and forward the request to the JSP page to render HTML. So, for this reason alone, you need to pass the request object to the service layer.

I would not, though, start writing raw Servlet classes. The work they do is very predictable and boilerplate, something that framework does very well. Fortunately, there are many available, time-tested candidates ( in the alphabetical order ): Apache Wicket, Java Server Faces, Spring to name a few.

Alternative answered 22/8, 2010 at 12:19 Comment(0)
A
5

IMHO, there is not much difference in case of web application if you look at it from the angle of responsibility assignment. However, keep the clarity in the layer. Keep anything purely for the presentation purpose in the presentation layer, like the control and code specific to the web controls. Just keep your entities in the business layer and all features (like add, edit, delete) etc in the business layer. However rendering them onto the browser to be handled in the presentation layer. For .Net, the ASP.NET MVC pattern is very good in terms of keeping the layers separated. Look into the MVC pattern.

Ambler answered 22/8, 2010 at 10:25 Comment(2)
can you go a bit explicit in what should go in servlet?Pedometer
The servlet should be the controller if you use MVC.Ambler
B
3

I have used the struts framework and find it fairly easy to learn. When using the struts framework each page of your site will have the following items.

1) An action which is used is called every time the HTML page is refreshed. The action should populate the data in the form when the page is first loaded and handles interactions between the web UI and the business layer. If you are using the jsp page to modify a mutable java object a copy of the java object should be stored in the form rather than the original so that the original data doesn't get modified unless the user saves the page.

2) The form which is used to transfer data between the action and the jsp page. This object should consist of a set of getter and setters for attributes that need to be accessible to the jsp file. The form also has a method to validate data before it gets persisted.

3) A jsp page which is used to render the final HTML of the page. The jsp page is a hybrid of HTML and special struts tags used to access and manipulate data in the form. Although struts allows users to insert Java code into jsp files you should be very cautious about doing that because it makes your code more difficult to read. Java code inside jsp files is difficult to debug and can not be unit tested. If you find yourself writing more than 4-5 lines of java code inside a jsp file the code should probably be moved to the action.

Bisayas answered 27/3, 2013 at 14:58 Comment(1)
Note: In struts 2 the Form object is referred to as a Model instead but works the same way as I described in my original answer.Bisayas
S
3

BalusC excellent answer covers most of the patterns for web applications.

Some application may require Chain-of-responsibility_pattern

In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.

Use case to use this pattern:

When handler to process a request(command) is unknown and this request can be sent to multiple objects. Generally you set successor to object. If current object can't handle the request or process the request partially and forward the same request to successor object.

Useful SE questions/articles:

Why would I ever use a Chain of Responsibility over a Decorator?

Common usages for chain of responsibility?

chain-of-responsibility-pattern from oodesign

chain_of_responsibility from sourcemaking

Seminary answered 4/4, 2016 at 12:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.