views:

301

answers:

7

I'm working on a simple Java application. Currently, I have two view, one for login in and another once you're logged in.

So my question is, once the user has logged in, should the controller of the login view creates the second view?

The problem is, the first controller needs to know every dependencies of the second view...

Edit : this is not a web application.

A: 

This is with Spring MVC?

If you're using Spring MVC 2.5 or lower without annotations, then at the end of handleRequest method, a ModelAndView object is returned. This contains the View information (whether it's the view itself as in a Redirect View, or just the name of a view). You could conditionally return a different view based on some logic. For example -

handleRequest(HttpServletRequest request, HttpServletResponse response) {
     // get user from session
     if (user.isLoggedIn())
         return new ModelAndView("loggedInView");
     else
         return new ModelAndView("notLoggedInView");
}

If this is a new project though, and you have access to Java 1.5, I would instead recommend using the annotations-based Spring MVC, because then you can return whatever the frik you want.

bpapa
Sorry, no. It's not a web app.
Subb
A: 

I think a factory might do the trick :

public class LoginViewFactory
{
    private depA;
    private depB;

    public LoginViewFactory(dependencyA, dependencyB)
    {
        depA = dependencyA;
        depB = dependencyB;
    }

    LoginView create()
    {
        return new LoginView(depA, depB);
    }
}


//and then pass this factory to my controller
new Controller(new LoginViewFactory(new A(), new B());

What do you think?

Subb
A: 

you should take the same approach as with Spring and have your controller return a view name. And then have another class handling creating the view based on the view name.

objects
A: 

I think your first "view" ( the login page ) can be rather considered a static page, as usually it will be the first accessible page, so probably there's no controller behind it.

But suppose there are some other pages in your web app and say you actually have a controller A ( kind of DispatchController ) that directs the user to the login page.

Once this page is submitted, you'll normally have a different controller ( say LoginController ) that will direct ( if login successful ) the user to your second view page ( logged in page )

So bottom line is : to me it looks like you need two controllers, each with a different responsability.

Billy
+1  A: 

I dont think the LoginController should create the SecondView at all. Once the user has logged in the LoginController should fire off an event that login was successful and any Controller that cares about that should take the appropriate action.

If you are using DI you ideally want to inject the View into a Controller.

Not quite sure what you mean with your last statement though so leaving that unanswered.

willcodejavaforfood
well, let's say my second view's constructor needs a model, the object responsible for creating this view (here, my controller) needs to know this model, inheriting the dependencies of the view.
Subb
+1  A: 

If your object needs to instantiate a class, but you don't want it to depend on the details of instantiating the class, inject a factory (as you've suggested).

I like to use interfaces so I can plug in different implementations of my dependencies. Here's an example:

public class RealLoginController implements LoginController {
    private LoginViewFactory viewFactory;

    public LoginController(LoginViewFactory viewFactory) {
        this.viewFactory = viewFactory;
    }

    public ModelAndView handleRequest(HttpServletRequest request,
                                      HttpServletResponse response) {
         if (isLoggedIn()) {
             return viewFactory.createLoggedInView();
         } else {
             return viewFactory.createLoggedOutView();
        }
    }

    // ...
}

public class RealLoggedInView implements LoginView {
    // Implementation for rendering stuff
}

public class RealLoggedOutView implements LoginView {
    // Implementation for rendering stuff
}


public interface LoginViewFactory {
    public LoginView createLoggedInView();
    public LoginView createLoggedInView();
}


public class RealLoginViewFactory implements LoginViewFactory {
    private FooModel fooEngine;
    private BarConfig barConfig;

    public RealLoginViewFactory(FooModel fooLayer, BarConfig barConfig) {
        this.fooEngine = fooEngine;
        this.barConfig = barConfig;
    }

    public LoginView createLoggedInView() {
        if (fooEngine.hasBaz()) {
            return new RealLoginView(barCongig.getQux());
        } else {
            return new RealLoginView(barCongig.getQux(),
                                     fooEngine.getBaz());
        }
    }

    public LoginView createLoggedOutView() {
        // ...
    }
}

public class RealLoginController implements LoginController {
    private LoginViewFactory viewFactory;

    public LoginController(LoginViewFactory viewFactory) {
        this.viewFactory = viewFactory;
    }

    public ModelAndView handleRequest(HttpServletRequest request,
                                      HttpServletResponse response) {
         if (isLoggedIn()) {
             return viewFactory.createLoggedInView();
         } else {
             return viewFactory.createLoggedOutView();
        }
    }

    // ...
}

You can then use the controller with any views you like:

public class LoginControllerTest {
    public void testSomething() {
        // ...
        controller = new RealLoginController(fakeViewFactory);
        assertHasTag("td#row1", controller.getRenderedStuff());
        // ...
    }
}

You may be able to avoid the problem (as bpappa suggests) if you don't need complex instantiation logic and your framework knows how to get dependencies for you by name.

Will Harris
A: 

If your login is a form POST (which it should be) I'd do a redirect to the home page after a successful login.

This is so that if the user hits refresh on the home page after logging in then they don't get that browser warning about resubmitting a POST.
The same thing applies if they click a link from the home page then click the back button.

In Spring you can do that with this syntax.

return new ModelAndView(new RedirectView(homepage, true), model);

The "homepage" parameter would be configured in your spring config and injected as a parameter - it should be a relative URL to the home page on your site.

Because you're redirecting the homepage controller is executed so this deals with your dependencies problem.

Darren Greaves