From fb86ca115677181d194cb2d5eb8a3276167090bf Mon Sep 17 00:00:00 2001 From: JanFidor <66260538+JanFidor@users.noreply.github.com> Date: Sat, 21 Jan 2023 10:45:07 +0100 Subject: [PATCH] feature: #2151 Feature/mvi pattern (#2177) * #2151 add module and pom.xml * #2151 add Calculator Actions * #2151 add remaining mvi pattern classes (actions already implemented) * #2151 add Main and unit tests * add README.md and class diagrams * #2151 add module and pom.xml * #2151 add Calculator Actions * #2151 add remaining mvi pattern classes (actions already implemented) * #2151 add Main and unit tests * add README.md and class diagrams * fixes for lint errors * #2151 add module and pom.xml * #2151 add Calculator Actions * #2151 add remaining mvi pattern classes (actions already implemented) * #2151 add Main and unit tests * add README.md and class diagrams * fixes for lint errors * use Lombok @Data decorator and decouple View from ViewModel * add comments and documentation * fix checkstyle, the smart switch syntax was breaking checkstyle, so I had to change it back --- model-view-intent/README.md | 245 ++++++++++++++++++ model-view-intent/etc/model-view-intent.png | Bin 0 -> 89303 bytes .../etc/model-view-intent.urm.puml | 74 ++++++ model-view-intent/pom.xml | 67 +++++ .../com/iluwatar/model/view/intent/App.java | 87 +++++++ .../model/view/intent/CalculatorModel.java | 23 ++ .../model/view/intent/CalculatorView.java | 74 ++++++ .../view/intent/CalculatorViewModel.java | 116 +++++++++ .../actions/AdditionCalculatorAction.java | 19 ++ .../view/intent/actions/CalculatorAction.java | 15 ++ .../actions/DivisionCalculatorAction.java | 19 ++ .../MultiplicationCalculatorAction.java | 19 ++ .../actions/SetVariableCalculatorAction.java | 30 +++ .../actions/SubtractionCalculatorAction.java | 19 ++ .../view/intent/actions/package-info.java | 6 + .../model/view/intent/package-info.java | 6 + .../iluwatar/model/view/intent/AppTest.java | 40 +++ .../view/intent/CalculatorViewModelTest.java | 83 ++++++ pom.xml | 1 + 19 files changed, 943 insertions(+) create mode 100644 model-view-intent/README.md create mode 100644 model-view-intent/etc/model-view-intent.png create mode 100644 model-view-intent/etc/model-view-intent.urm.puml create mode 100644 model-view-intent/pom.xml create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/App.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorModel.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorView.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/CalculatorViewModel.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/AdditionCalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/CalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/DivisionCalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/MultiplicationCalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SetVariableCalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/SubtractionCalculatorAction.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/actions/package-info.java create mode 100644 model-view-intent/src/main/java/com/iluwatar/model/view/intent/package-info.java create mode 100644 model-view-intent/src/test/java/com/iluwatar/model/view/intent/AppTest.java create mode 100644 model-view-intent/src/test/java/com/iluwatar/model/view/intent/CalculatorViewModelTest.java 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 0000000000000000000000000000000000000000..0c1648ff4b3d347c339243b974f7b0349ded4ba0 GIT binary patch literal 89303 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%0A>3&jHV*umY|njvCidAA@N{$o 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 "-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