mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-14 08:58:26 +00:00
* #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:
@@ -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
|
||||

|
||||
|
||||
**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
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
+116
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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();
|
||||
}
|
||||
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+6
@@ -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[]{}));
|
||||
}
|
||||
}
|
||||
+83
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user