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
This commit is contained in:
JanFidor
2023-01-21 10:45:07 +01:00
committed by GitHub
parent 6b0a90ee61
commit fb86ca1156
19 changed files with 943 additions and 0 deletions
+245
View File
@@ -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)
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

@@ -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
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>model-view-intent</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.model.view.controller.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -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() {
}
}
@@ -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;
}
@@ -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));
}
}
@@ -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()
);
}
}
@@ -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;
}
}
@@ -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();
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
@@ -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;
@@ -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[]{}));
}
}
@@ -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<CalculatorAction> 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<CalculatorAction> actions = List.of(
new SetVariableCalculatorAction(10.0)
);
CalculatorModel model = modelAfterExecutingActions(actions);
assert model.getVariable() == 10.0 && model.getOutput() == 0;
}
@Test
void testAddition() {
List<CalculatorAction> 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<CalculatorAction> 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<CalculatorAction> 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<CalculatorAction> 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;
}
}
+1
View File
@@ -198,6 +198,7 @@
<module>metadata-mapping</module>
<module>service-to-worker</module>
<module>client-session</module>
<module>model-view-intent</module>
<module>embedded-value</module>
<module>currying</module>
<module>serialized-entity</module>