diff --git a/model-view-intent/README.md b/model-view-intent/README.md new file mode 100644 index 000000000..9b00b5a73 --- /dev/null +++ b/model-view-intent/README.md @@ -0,0 +1,245 @@ +--- +title: Model-View-Controller +category: Architectural +language: en +tags: + - Decoupling + - Encapsulation +--- + +## Intent +MVI is a derivation of the original MVC architectural pattern. Instead of working with a +proactive controller MVI works with the reactive component called intent: it's a component +which translates user input events into model updates. + +## Explanation + +> MVI is a Reactive Architecture Pattern which is short for Model -View-Intent. +It introduces two new concepts: the intent and the state. +UI might have different states — Loading State, Fetch Data State, Error State, +and user events are submitted in the form of an Intent. + +* [Stateful Android Apps With MVI (MODEL — VIEW — INTENT)](https://medium.com/huawei-developers/stateful-android-apps-with-mvi-architecture-model-view-intent-d106b09bd967) + +## Class diagram +![alt text](./etc/model-view-intent.png "Model-View-Intent") + +**Programmatic Example** + +CalculatorAction defines our Intent in MVI for user interactions. It has to be an interface +instead of enum, so that we can pass parameters to certain children. +```java +public interface CalculatorAction { + + /** + * Makes identifying action trivial. + * + * @return subclass tag. + * */ + String tag(); +} +``` + +CalculatorModel defines the state of our view or in out case, variable and output of the calculator. +```java +@Data +public class CalculatorModel { + + /** + * Current calculator variable used for operations. + **/ + final Double variable; + + /** + * Current calculator output -> is affected by operations. + **/ + final Double output; +} +``` + + +CalculatorView will serve as a mock view which will expose potential user actions and +display calculator state -> output and current variable +```java +@Slf4j +public class CalculatorView { + + /** + * View model param handling the operations. + * */ + private final CalculatorViewModel viewModel = new CalculatorViewModel(); + + /** + * Display current view model output with logger. + * */ + void displayTotal() { + LOGGER.info( + "Total value = {}", + viewModel.getCalculatorModel().getOutput().toString() + ); + } + + /** + * Handle addition action. + * */ + void add() { + viewModel.handleAction(new AdditionCalculatorAction()); + } + + /** + * Handle subtraction action. + * */ + void subtract() { + viewModel.handleAction(new SubtractionCalculatorAction()); + } + + /** + * Handle multiplication action. + * */ + void multiply() { + viewModel.handleAction(new MultiplicationCalculatorAction()); + } + + /** + * Handle division action. + * */ + void divide() { + viewModel.handleAction(new DivisionCalculatorAction()); + } + + /** + * Handle setting new variable action. + * + * @param value -> new calculator variable. + * */ + void setVariable(final Double value) { + viewModel.handleAction(new SetVariableCalculatorAction(value)); + } +} +``` + +Finally, ViewModel handles the exposed events with the handleAction(event) method, which delegates +the specific handling to private methods. Initially calculator output and variable are equal to 0. +```java +public final class CalculatorViewModel { + + /** + * Current calculator model (can be changed). + */ + private CalculatorModel model = + new CalculatorModel(0.0, 0.0); + + /** + * Handle calculator action. + * + * @param action -> transforms calculator model. + */ + void handleAction(final CalculatorAction action) { + switch (action.tag()) { + case AdditionCalculatorAction.TAG -> add(); + case SubtractionCalculatorAction.TAG -> subtract(); + case MultiplicationCalculatorAction.TAG -> multiply(); + case DivisionCalculatorAction.TAG -> divide(); + case SetVariableCalculatorAction.TAG -> { + SetVariableCalculatorAction setVariableAction = + (SetVariableCalculatorAction) action; + setVariable(setVariableAction.getVariable()); + } + default -> { + } + } + } + + /** + * Getter. + * + * @return current calculator model. + */ + public CalculatorModel getCalculatorModel() { + return model; + } + + /** + * Set new calculator model variable. + * + * @param variable -> value of new calculator model variable. + */ + private void setVariable(final Double variable) { + model = new CalculatorModel( + variable, + model.getOutput() + ); + } + + /** + * Add variable to model output. + */ + private void add() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() + model.getVariable() + ); + } + + /** + * Subtract variable from model output. + */ + private void subtract() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() - model.getVariable() + ); + } + + /** + * Multiply model output by variable. + */ + private void multiply() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() * model.getVariable() + ); + } + + /** + * Divide model output by variable. + */ + private void divide() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() / model.getVariable() + ); + } +} +``` + +## Applicability +Use the Model-View-Intent pattern when + +* You want to clearly separate the domain data from its user interface representation +* You want to minimise the public api of the view model + +## Known uses +A popular architecture pattern in android. The small public api is particularly powerful +with the new Android Compose UI, as you can pass a single method (viewModel::handleEvent) +to all Composables(parts of UI) as a callback for user input event. + +## Consequences +Pros: +* Encapsulation +* Separation of concerns +* Clear list of all possible user events + +Cons: +* More boilerplate code compared to alternatives (especially in Java) + +## Related patterns +MVC: +* [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) +* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) + +## Credits + +* [Model View Intent: a new Android Architecture Pattern](https://apiumacademy.com/blog/model-view-intent-pattern/) +* [MVI Architecture for Android Tutorial](https://www.kodeco.com/817602-mvi-architecture-for-android-tutorial-getting-started) + diff --git a/model-view-intent/etc/model-view-intent.png b/model-view-intent/etc/model-view-intent.png new file mode 100644 index 000000000..0c1648ff4 Binary files /dev/null and b/model-view-intent/etc/model-view-intent.png differ diff --git a/model-view-intent/etc/model-view-intent.urm.puml b/model-view-intent/etc/model-view-intent.urm.puml new file mode 100644 index 000000000..8b1f7c314 --- /dev/null +++ b/model-view-intent/etc/model-view-intent.urm.puml @@ -0,0 +1,74 @@ +@startuml +package com.iluwatar.model.view.intent.actions { + class AdditionCalculatorAction { + + TAG : String {static} + + AdditionCalculatorAction() + + tag() : String + } + interface CalculatorAction { + + tag() : String {abstract} + } + class DivisionCalculatorAction { + + TAG : String {static} + + DivisionCalculatorAction() + + tag() : String + } + class MultiplicationCalculatorAction { + + TAG : String {static} + + MultiplicationCalculatorAction() + + tag() : String + } + class SetVariableCalculatorAction { + + TAG : String {static} + + variable : Double + + SetVariableCalculatorAction(variable : Double) + + tag() : String + } + class SubtractionCalculatorAction { + + TAG : String {static} + + SubtractionCalculatorAction() + + tag() : String + } +} +package com.iluwatar.model.view.intent { + class App { + + App() + + main(args : String[]) {static} + } + class CalculatorModel { + + output : Double + + variable : Double + + CalculatorModel(output : Double, variable : Double) + + copy(output : Double, variable : Double) : CalculatorModel + } + class CalculatorView { + - LOGGER : Logger {static} + ~ viewModel : CalculatorViewModel + + CalculatorView() + ~ add() + ~ displayTotal() + ~ divide() + ~ multiply() + ~ setVariable(value : Double) + ~ subtract() + } + class CalculatorViewModel { + - model : CalculatorModel + + CalculatorViewModel() + - add() + - divide() + + getCalculatorModel() : CalculatorModel + ~ handleAction(action : CalculatorAction) + - multiply() + - setVariable(variable : Double) + - subtract() + } +} +CalculatorView --> "-viewModel" CalculatorViewModel +CalculatorViewModel --> "-model" CalculatorModel +AdditionCalculatorAction ..|> CalculatorAction +DivisionCalculatorAction ..|> CalculatorAction +MultiplicationCalculatorAction ..|> CalculatorAction +SetVariableCalculatorAction ..|> CalculatorAction +SubtractionCalculatorAction ..|> CalculatorAction +@enduml \ No newline at end of file diff --git a/model-view-intent/pom.xml b/model-view-intent/pom.xml new file mode 100644 index 000000000..9ca57f881 --- /dev/null +++ b/model-view-intent/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + model-view-intent + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.mockito + mockito-core + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.model.view.controller.App + + + + + + + + + diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/App.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/App.java new file mode 100644 index 000000000..c318708ad --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/App.java @@ -0,0 +1,87 @@ +/* + * 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.model.view.intent; + +/** + * Model-View-Intent is a pattern for implementing user interfaces. + * Its main advantage over MVVM which it closely mirrors is a + * minimal public api with which user events can be exposed to the ViewModel. + * In case of the MVI every event is exposed by using a single method + * with 1 argument which implements UserEvent interface. + * Specific parameters can be expressed as its parameters. In this case, + * we'll be using MVI to implement a simple calculator + * with +, -, /, * operations and the ability to set the variable. + * It's important to note, that every user action happens through the + * view, we never interact with the ViewModel directly. + */ +public final class App { + + + /** + * To avoid magic value lint error. + */ + private static final double RANDOM_VARIABLE = 10.0; + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(final String[] args) { + // create model, view and controller + + // initialize calculator view, output and variable = 0 + var view = new CalculatorView(new CalculatorViewModel()); + var variable1 = RANDOM_VARIABLE; + + // calculator variable = RANDOM_VARIABLE -> 10.0 + view.setVariable(variable1); + + // add calculator variable to output -> calculator output = 10.0 + view.add(); + view.displayTotal(); // display output + + variable1 = 2.0; + view.setVariable(variable1); // calculator variable = 2.0 + + // subtract calculator variable from output -> calculator output = 8 + view.subtract(); + + // divide calculator output by variable -> calculator output = 4.0 + view.divide(); + + // multiply calculator output by variable -> calculator output = 8.0 + view.multiply(); + view.displayTotal(); + } + + /** + * Avoid default constructor lint error. + */ + private App() { + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorModel.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorModel.java new file mode 100644 index 000000000..8e11322fc --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorModel.java @@ -0,0 +1,23 @@ +package com.iluwatar.model.view.intent; + +import lombok.Data; +import lombok.Getter; + +/** + * Current state of calculator. + */ +@Data +public class CalculatorModel { + + /** + * Current calculator variable used for operations. + **/ + @Getter + private final Double variable; + + /** + * Current calculator output -> is affected by operations. + **/ + @Getter + private final Double output; +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorView.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorView.java new file mode 100644 index 000000000..dfaf15468 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorView.java @@ -0,0 +1,74 @@ +package com.iluwatar.model.view.intent; + +import com.iluwatar.model.view.intent.actions.AdditionCalculatorAction; +import com.iluwatar.model.view.intent.actions.DivisionCalculatorAction; +import com.iluwatar.model.view.intent.actions.MultiplicationCalculatorAction; +import com.iluwatar.model.view.intent.actions.SetVariableCalculatorAction; +import com.iluwatar.model.view.intent.actions.SubtractionCalculatorAction; +import lombok.Data; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +/** + * Exposes changes to the state of calculator + * to {@link CalculatorViewModel} through + * {@link com.iluwatar.model.view.intent.actions.CalculatorAction} + * and displays its updated {@link CalculatorModel}. + */ +@Slf4j +@Data +public class CalculatorView { + + /** + * View model param handling the operations. + */ + @Getter + private final CalculatorViewModel viewModel; + + /** + * Display current view model output with logger. + */ + void displayTotal() { + LOGGER.info( + "Total value = {}", + viewModel.getCalculatorModel().getOutput().toString() + ); + } + + /** + * Handle addition action. + */ + void add() { + viewModel.handleAction(new AdditionCalculatorAction()); + } + + /** + * Handle subtraction action. + */ + void subtract() { + viewModel.handleAction(new SubtractionCalculatorAction()); + } + + /** + * Handle multiplication action. + */ + void multiply() { + viewModel.handleAction(new MultiplicationCalculatorAction()); + } + + /** + * Handle division action. + */ + void divide() { + viewModel.handleAction(new DivisionCalculatorAction()); + } + + /** + * Handle setting new variable action. + * + * @param value -> new calculator variable. + */ + void setVariable(final Double value) { + viewModel.handleAction(new SetVariableCalculatorAction(value)); + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorViewModel.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorViewModel.java new file mode 100644 index 000000000..d8c2a30e7 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorViewModel.java @@ -0,0 +1,116 @@ +package com.iluwatar.model.view.intent; + +import com.iluwatar.model.view.intent.actions.AdditionCalculatorAction; +import com.iluwatar.model.view.intent.actions.CalculatorAction; +import com.iluwatar.model.view.intent.actions.DivisionCalculatorAction; +import com.iluwatar.model.view.intent.actions.MultiplicationCalculatorAction; +import com.iluwatar.model.view.intent.actions.SetVariableCalculatorAction; +import com.iluwatar.model.view.intent.actions.SubtractionCalculatorAction; + +/** + * Handle transformations to {@link CalculatorModel} + * based on intercepted {@link CalculatorAction}. + */ +public final class CalculatorViewModel { + + /** + * Current calculator model (can be changed). + */ + private CalculatorModel model = + new CalculatorModel(0.0, 0.0); + + /** + * Handle calculator action. + * + * @param action -> transforms calculator model. + */ + void handleAction(final CalculatorAction action) { + switch (action.tag()) { + case AdditionCalculatorAction.TAG: + add(); + break; + + case SubtractionCalculatorAction.TAG: + subtract(); + break; + + case MultiplicationCalculatorAction.TAG: + multiply(); + break; + + case DivisionCalculatorAction.TAG: + divide(); + break; + + case SetVariableCalculatorAction.TAG: + SetVariableCalculatorAction setVariableAction = + (SetVariableCalculatorAction) action; + setVariable(setVariableAction.getVariable()); + break; + + default: + break; + } + } + + /** + * Getter. + * + * @return current calculator model. + */ + public CalculatorModel getCalculatorModel() { + return model; + } + + /** + * Set new calculator model variable. + * + * @param variable -> value of new calculator model variable. + */ + private void setVariable(final Double variable) { + model = new CalculatorModel( + variable, + model.getOutput() + ); + } + + /** + * Add variable to model output. + */ + private void add() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() + model.getVariable() + ); + } + + /** + * Subtract variable from model output. + */ + private void subtract() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() - model.getVariable() + ); + } + + /** + * Multiply model output by variable. + */ + private void multiply() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() * model.getVariable() + ); + } + + /** + * Divide model output by variable. + */ + private void divide() { + model = new CalculatorModel( + model.getVariable(), + model.getOutput() / model.getVariable() + ); + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/AdditionCalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/AdditionCalculatorAction.java new file mode 100644 index 000000000..5647232e0 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/AdditionCalculatorAction.java @@ -0,0 +1,19 @@ +package com.iluwatar.model.view.intent.actions; + +/** + * Addition {@link CalculatorAction}. + * */ +public class AdditionCalculatorAction implements CalculatorAction { + /** + * Subclass tag. + * */ + public static final String TAG = "ADDITION"; + + /** + * Makes checking subclass type trivial. + * */ + @Override + public String tag() { + return TAG; + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/CalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/CalculatorAction.java new file mode 100644 index 000000000..70477409a --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/CalculatorAction.java @@ -0,0 +1,15 @@ +package com.iluwatar.model.view.intent.actions; + +/** + * Defines what outside interactions can be consumed by view model. + * */ +public interface CalculatorAction { + + /** + * Makes identifying action trivial. + * + * @return subclass tag. + * */ + String tag(); +} + diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/DivisionCalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/DivisionCalculatorAction.java new file mode 100644 index 000000000..9da56c6cc --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/DivisionCalculatorAction.java @@ -0,0 +1,19 @@ +package com.iluwatar.model.view.intent.actions; + +/** + * Division {@link CalculatorAction}. + * */ +public class DivisionCalculatorAction implements CalculatorAction { + /** + * Subclass tag. + * */ + public static final String TAG = "DIVISION"; + + /** + * Makes checking subclass type trivial. + * */ + @Override + public String tag() { + return TAG; + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/MultiplicationCalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/MultiplicationCalculatorAction.java new file mode 100644 index 000000000..1bf9f4c86 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/MultiplicationCalculatorAction.java @@ -0,0 +1,19 @@ +package com.iluwatar.model.view.intent.actions; + +/** + * Multiplication {@link CalculatorAction}. + * */ +public class MultiplicationCalculatorAction implements CalculatorAction { + /** + * Subclass tag. + * */ + public static final String TAG = "MULTIPLICATION"; + + /** + * Makes checking subclass type trivial. + * */ + @Override + public String tag() { + return TAG; + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SetVariableCalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SetVariableCalculatorAction.java new file mode 100644 index 000000000..564609ccc --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SetVariableCalculatorAction.java @@ -0,0 +1,30 @@ +package com.iluwatar.model.view.intent.actions; + +import lombok.Data; +import lombok.Getter; + +/** + * SetVariable {@link CalculatorAction}. + */ +@Data +public final class SetVariableCalculatorAction implements CalculatorAction { + + /** + * Subclass tag. + */ + public static final String TAG = "SET_VARIABLE"; + + /** + * Used by {@link com.iluwatar.model.view.intent.CalculatorViewModel}. + */ + @Getter + private final Double variable; + + /** + * Makes checking subclass type trivial. + */ + @Override + public String tag() { + return TAG; + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SubtractionCalculatorAction.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SubtractionCalculatorAction.java new file mode 100644 index 000000000..c8b31e3b8 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SubtractionCalculatorAction.java @@ -0,0 +1,19 @@ +package com.iluwatar.model.view.intent.actions; + +/** + * Subtraction {@link CalculatorAction}. + * */ +public class SubtractionCalculatorAction implements CalculatorAction { + /** + * Subclass tag. + * */ + public static final String TAG = "SUBTRACTION"; + + /** + * Makes checking subclass type trivial. + * */ + @Override + public String tag() { + return TAG; + } +} diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/package-info.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/package-info.java new file mode 100644 index 000000000..3ce2a4662 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/package-info.java @@ -0,0 +1,6 @@ +/** + * Handle actions for {@link com.iluwatar.model.view.intent.CalculatorModel} + * defined by {@link com.iluwatar.model.view.intent.actions.CalculatorAction}. + */ + +package com.iluwatar.model.view.intent.actions; diff --git a/model-view-intent/src/main/java/com/iluwatar/model/view/intent/package-info.java b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/package-info.java new file mode 100644 index 000000000..ddfcb9827 --- /dev/null +++ b/model-view-intent/src/main/java/com/iluwatar/model/view/intent/package-info.java @@ -0,0 +1,6 @@ +/** + * Define Model, View and ViewModel. + * Use them in {@link com.iluwatar.model.view.intent.App} + */ + +package com.iluwatar.model.view.intent; diff --git a/model-view-intent/src/test/java/com/iluwatar/model/view/intent/AppTest.java b/model-view-intent/src/test/java/com/iluwatar/model/view/intent/AppTest.java new file mode 100644 index 000000000..6adc84c64 --- /dev/null +++ b/model-view-intent/src/test/java/com/iluwatar/model/view/intent/AppTest.java @@ -0,0 +1,40 @@ +/* + * 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.model.view.intent; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * Application test + */ +class AppTest { + + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/model-view-intent/src/test/java/com/iluwatar/model/view/intent/CalculatorViewModelTest.java b/model-view-intent/src/test/java/com/iluwatar/model/view/intent/CalculatorViewModelTest.java new file mode 100644 index 000000000..91a45e83b --- /dev/null +++ b/model-view-intent/src/test/java/com/iluwatar/model/view/intent/CalculatorViewModelTest.java @@ -0,0 +1,83 @@ +package com.iluwatar.model.view.intent; + +import com.iluwatar.model.view.intent.actions.*; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class CalculatorViewModelTest { + + private CalculatorModel modelAfterExecutingActions(List actions) { + CalculatorViewModel viewModel = new CalculatorViewModel(); + for (CalculatorAction action : actions) { + viewModel.handleAction(action); + } + return viewModel.getCalculatorModel(); + } + + @Test + void testSetup() { + CalculatorModel model = modelAfterExecutingActions(new ArrayList<>()); + assert model.getVariable() == 0 && model.getOutput() == 0; + } + + @Test + void testSetVariable() { + List actions = List.of( + new SetVariableCalculatorAction(10.0) + ); + CalculatorModel model = modelAfterExecutingActions(actions); + assert model.getVariable() == 10.0 && model.getOutput() == 0; + } + + @Test + void testAddition() { + List actions = List.of( + new SetVariableCalculatorAction(2.0), + new AdditionCalculatorAction(), + new AdditionCalculatorAction(), + new SetVariableCalculatorAction(7.0), + new AdditionCalculatorAction() + ); + CalculatorModel model = modelAfterExecutingActions(actions); + assert model.getVariable() == 7.0 && model.getOutput() == 11.0; + } + + @Test + void testSubtraction() { + List actions = List.of( + new SetVariableCalculatorAction(2.0), + new AdditionCalculatorAction(), + new AdditionCalculatorAction(), + new SubtractionCalculatorAction() + ); + CalculatorModel model = modelAfterExecutingActions(actions); + assert model.getVariable() == 2.0 && model.getOutput() == 2.0; + } + + @Test + void testMultiplication() { + List actions = List.of( + new SetVariableCalculatorAction(2.0), + new AdditionCalculatorAction(), + new AdditionCalculatorAction(), + new MultiplicationCalculatorAction() + ); + CalculatorModel model = modelAfterExecutingActions(actions); + assert model.getVariable() == 2.0 && model.getOutput() == 8.0; + } + + @Test + void testDivision() { + List actions = List.of( + new SetVariableCalculatorAction(2.0), + new AdditionCalculatorAction(), + new AdditionCalculatorAction(), + new SetVariableCalculatorAction(2.0), + new DivisionCalculatorAction() + ); + CalculatorModel model = modelAfterExecutingActions(actions); + assert model.getVariable() == 2.0 && model.getOutput() == 2.0; + } +} diff --git a/pom.xml b/pom.xml index 541c56afd..54849794a 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,7 @@ metadata-mapping service-to-worker client-session + model-view-intent embedded-value currying serialized-entity