diff --git a/front-controller/README.md b/front-controller/README.md index 2a2a96113dbb..44f61abeeac0 100644 --- a/front-controller/README.md +++ b/front-controller/README.md @@ -36,7 +36,7 @@ Wikipedia says The Front Controller design pattern is a pattern that provides a centralized entry point for handling all requests in a web application. It ensures that request handling is managed consistently and efficiently across an application. -In the provided code, we can see an example of the Front Controller pattern in the `App` and `FrontController` classes. +In the provided code, we can see an example of the Front Controller pattern in the `App`, `FrontController` and `Dispatcher` classes. The `App` class is the entry point of the application. It creates an instance of `FrontController` and uses it to handle various requests. @@ -52,33 +52,55 @@ public class App { } ``` -The `FrontController` class is the front controller in this example. It handles all requests and routes them to the appropriate handlers. +The `FrontController` class is the front controller in this example. It handles all requests and delegates them to the `Dispatcher`. ```java public class FrontController { - public void handleRequest(String request) { - Command command; - - switch (request) { - case "Archer": - command = new ArcherCommand(); - break; - case "Catapult": - command = new CatapultCommand(); - break; - default: - command = new UnknownCommand(); + private final Dispatcher dispatcher; + + public FrontController() { + this.dispatcher = new Dispatcher(); } + public void handleRequest(String request) { + dispatcher.dispatch(request); + } +} +``` + +The `Dispatcher` class is responsible for handling the dispatching of requests to the appropriate command. It retrieves the corresponding command based on the request and invokes the command's process method to handle the business logic. + +```java +public class Dispatcher { + + public void dispatch(String request) { + var command = getCommand(request); command.process(); } + + Command getCommand(String request) { + var commandClass = getCommandClass(request); + try { + return (Command) commandClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new ApplicationException(e); + } + } + + static Class getCommandClass(String request) { + try { + return Class.forName("com.iluwatar.front.controller." + request + "Command"); + } catch (ClassNotFoundException e) { + return UnknownCommand.class; + } + } } ``` -In this example, when a request is received, the `FrontController` creates a command object based on the request and calls its `process` method. The command object is responsible for handling the request and rendering the appropriate view. +In this example, when a request is received, the `FrontController` delegates the request to the `Dispatcher`, which creates a command object based on the request and calls its `process` method. The command object is responsible for handling the request and rendering the appropriate view. -This is a basic example of the Front Controller pattern, where all requests are handled by a single controller, ensuring consistent and efficient request handling. +This is a basic example of the Front Controller pattern, where all requests are handled by a single controller and dispatcher, ensuring consistent and efficient request handling. ## Class diagram diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/App.java b/front-controller/src/main/java/com/iluwatar/front/controller/App.java index 06922251f1b3..88f24360930a 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/App.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/App.java @@ -25,14 +25,14 @@ package com.iluwatar.front.controller; /** - * The Front Controller is a presentation tier pattern. Essentially it defines a controller that + * The Front Controller is a presentation tier pattern. Essentially, it defines a controller that * handles all requests for a website. * *

The Front Controller pattern consolidates request handling through a single handler object ( - * {@link FrontController}). This object can carry out the common the behavior such as - * authorization, request logging and routing requests to corresponding views. + * {@link FrontController}). This object can carry out common behavior such as authorization, + * request logging and routing requests to corresponding views. * - *

Typically the requests are mapped to command objects ({@link Command}) which then display the + *

Typically, the requests are mapped to command objects ({@link Command}) which then display the * correct view ({@link View}). * *

In this example we have implemented two views: {@link ArcherView} and {@link CatapultView}. diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/Dispatcher.java b/front-controller/src/main/java/com/iluwatar/front/controller/Dispatcher.java new file mode 100644 index 000000000000..8b9644ab8b3e --- /dev/null +++ b/front-controller/src/main/java/com/iluwatar/front/controller/Dispatcher.java @@ -0,0 +1,72 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +/** + * The Dispatcher class is responsible for handling the dispatching of requests to the appropriate + * command. It retrieves the corresponding command based on the request and invokes the command's + * process method to handle the business logic. + */ +public class Dispatcher { + + /** + * Dispatches the request to the appropriate command. + * + * @param request the request to be handled + */ + public void dispatch(String request) { + var command = getCommand(request); + command.process(); + } + + /** + * Retrieves the appropriate command instance for the given request. + * + * @param request the request to be handled + * @return the command instance corresponding to the request + */ + Command getCommand(String request) { + var commandClass = getCommandClass(request); + try { + return (Command) commandClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new ApplicationException(e); + } + } + + /** + * Retrieves the Class object for the command corresponding to the given request. + * + * @param request the request to be handled + * @return the Class object of the command corresponding to the request + */ + static Class getCommandClass(String request) { + try { + return Class.forName("com.iluwatar.front.controller." + request + "Command"); + } catch (ClassNotFoundException e) { + return UnknownCommand.class; + } + } +} \ No newline at end of file diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java index 995f917b3fd5..bc8b4e344d4f 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java @@ -25,30 +25,19 @@ package com.iluwatar.front.controller; /** - * FrontController is the handler class that takes in all the requests and renders the correct - * response. + * The FrontController is responsible for handling all incoming requests. It delegates + * the processing of requests to the Dispatcher, which then determines the appropriate + * command and view to render the correct response. */ public class FrontController { - public void handleRequest(String request) { - var command = getCommand(request); - command.process(); - } + private final Dispatcher dispatcher; - private Command getCommand(String request) { - var commandClass = getCommandClass(request); - try { - return (Command) commandClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - throw new ApplicationException(e); - } + public FrontController() { + this.dispatcher = new Dispatcher(); } - private static Class getCommandClass(String request) { - try { - return Class.forName("com.iluwatar.front.controller." + request + "Command"); - } catch (ClassNotFoundException e) { - return UnknownCommand.class; - } + public void handleRequest(String request) { + dispatcher.dispatch(request); } } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/DispatcherTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/DispatcherTest.java new file mode 100644 index 000000000000..c60aa899d335 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/DispatcherTest.java @@ -0,0 +1,91 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class DispatcherTest { + + private Dispatcher dispatcher; + + @BeforeEach + public void setUp() { + dispatcher = new Dispatcher(); + } + + @Test + void testDispatchKnownCommand() { + Command mockCommand = mock(ArcherCommand.class); + dispatcher = spy(dispatcher); + doReturn(mockCommand).when(dispatcher).getCommand("Archer"); + + dispatcher.dispatch("Archer"); + + verify(mockCommand, times(1)).process(); + } + + @Test + void testDispatchUnknownCommand() { + Command mockCommand = mock(UnknownCommand.class); + dispatcher = spy(dispatcher); + doReturn(mockCommand).when(dispatcher).getCommand("Unknown"); + + dispatcher.dispatch("Unknown"); + + verify(mockCommand, times(1)).process(); + } + + @Test + void testGetCommandKnown() { + Command command = dispatcher.getCommand("Archer"); + assertNotNull(command); + assertTrue(command instanceof ArcherCommand); + } + + @Test + void testGetCommandUnknown() { + Command command = dispatcher.getCommand("Unknown"); + assertNotNull(command); + assertTrue(command instanceof UnknownCommand); + } + + @Test + void testGetCommandClassKnown() { + Class commandClass = Dispatcher.getCommandClass("Archer"); + assertNotNull(commandClass); + assertEquals(ArcherCommand.class, commandClass); + } + + @Test + void testGetCommandClassUnknown() { + Class commandClass = Dispatcher.getCommandClass("Unknown"); + assertNotNull(commandClass); + assertEquals(UnknownCommand.class, commandClass); + } +}