Compare commits
9 Commits
all-contri
...
1.23.0
Author | SHA1 | Date | |
---|---|---|---|
687648af0a | |||
96aa21d0e8 | |||
b8f83c326d | |||
15d795bf8a | |||
3754c66604 | |||
6d83ceba28 | |||
015b418114 | |||
0cba307844 | |||
a66edc84a0 |
@ -1167,6 +1167,15 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "stefanbirkner",
|
||||||
|
"name": "Stefan Birkner",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/711349?v=4",
|
||||||
|
"profile": "https://www.stefan-birkner.de",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 4,
|
"contributorsPerLine": 4,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
@ -261,6 +261,9 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
|
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- markdownlint-enable -->
|
<!-- markdownlint-enable -->
|
||||||
|
@ -9,20 +9,26 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
|
|
||||||
Handle/Body
|
Handle/Body
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
|
||||||
Decouple an abstraction from its implementation so that the two can vary independently.
|
Decouple an abstraction from its implementation so that the two can vary independently.
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real world example
|
Real world example
|
||||||
|
|
||||||
> Consider you have a weapon with different enchantments and you are supposed to allow mixing different weapons with different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments or would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the second.
|
> Consider you have a weapon with different enchantments, and you are supposed to allow mixing
|
||||||
|
> different weapons with different enchantments. What would you do? Create multiple copies of each
|
||||||
|
> of the weapons for each of the enchantments or would you just create separate enchantment and set
|
||||||
|
> it for the weapon as needed? Bridge pattern allows you to do the second.
|
||||||
|
|
||||||
In Plain Words
|
In Plain Words
|
||||||
|
|
||||||
> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
|
> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed
|
||||||
|
> from a hierarchy to another object with a separate hierarchy.
|
||||||
|
|
||||||
Wikipedia says
|
Wikipedia says
|
||||||
|
|
||||||
@ -30,7 +36,7 @@ Wikipedia says
|
|||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
Translating our weapon example from above. Here we have the `Weapon` hierarchy
|
Translating our weapon example from above. Here we have the `Weapon` hierarchy:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Weapon {
|
public interface Weapon {
|
||||||
@ -105,7 +111,7 @@ public class Hammer implements Weapon {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And the separate enchantment hierarchy
|
Here's the separate enchantment hierarchy:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Enchantment {
|
public interface Enchantment {
|
||||||
@ -151,7 +157,7 @@ public class SoulEatingEnchantment implements Enchantment {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And both the hierarchies in action
|
Here are both hierarchies in action:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var enchantedSword = new Sword(new SoulEatingEnchantment());
|
var enchantedSword = new Sword(new SoulEatingEnchantment());
|
||||||
@ -178,18 +184,21 @@ hammer.unwield();
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
Use the Bridge pattern when
|
Use the Bridge pattern when
|
||||||
|
|
||||||
* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
|
* You want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
|
||||||
* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently
|
* Both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently.
|
||||||
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
|
* Changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
|
||||||
* you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies
|
* You have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies.
|
||||||
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
* You want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
|
||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
|
|
||||||
* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java)
|
* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
--- # this is so called 'Yaml Front Matter', read up on it here: http://jekyllrb.com/docs/frontmatter/
|
---
|
||||||
layout: pattern
|
layout: pattern
|
||||||
title: Filterer
|
title: Filterer
|
||||||
folder: filterer
|
folder: filterer
|
||||||
@ -11,27 +11,33 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Name / classification
|
## Name / classification
|
||||||
|
|
||||||
Filterer
|
Filterer
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
The intent of this design pattern is to introduce a functional interface that will add a functionality for container-like objects to easily return filtered versions of themselves.
|
|
||||||
|
The intent of this design pattern is to introduce a functional interface that will add a
|
||||||
|
functionality for container-like objects to easily return filtered versions of themselves.
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real world example
|
Real world example
|
||||||
|
|
||||||
> We are designing a threat(malware) detection system. We can have different types of threats and systems. We have a requirement that
|
> We are designing a threat (malware) detection software which can analyze target systems for
|
||||||
> system should be aware of threats that are present in it. In the design we have to take into consideration that new Threat types can be
|
> threats that are present in it. In the design we have to take into consideration that new
|
||||||
> added later. Also there is a requirement that a system can filter itself based on the threats that it possesses (system acts as container-like object for threats).
|
> Threat types can be added later. Additionally, there is a requirement that the threat detection
|
||||||
>
|
> system can filter the detected threats based on different criteria (the target system acts as
|
||||||
|
> container-like object for threats).
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
> We need to be able to filter different types of systems(container-like objects) based on properties of Threats that they contain.
|
> Filterer pattern is a design pattern that helps container-like objects return filtered versions
|
||||||
> Adding new properties for Threats should be easy (we still need the ability to filter by those new properties).
|
> of themselves.
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem` interfaces.
|
To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem`
|
||||||
|
interfaces.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface Threat {
|
public interface Threat {
|
||||||
@ -47,19 +53,26 @@ public interface ThreatAwareSystem {
|
|||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Notice the `filtered` method that returns instance of `Filterer` interface which is defined as :
|
|
||||||
|
Notice the `filtered` method that returns instance of `Filterer` interface which is defined as:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface Filterer<G, E> {
|
public interface Filterer<G, E> {
|
||||||
G by(Predicate<? super E> predicate);
|
G by(Predicate<? super E> predicate);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
it is used to fulfill the requirement for system to be able to filter itself based on threat properties.
|
|
||||||
The container-like object (`ThreatAwareSystem` in our case) needs to have a method that returns an instance of `Filterer`. This helper interface gives
|
|
||||||
ability to covariantly specify a lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the container-like objects.
|
|
||||||
|
|
||||||
In our example we will be able to pass a predicate that takes `? extends Threat` object and return `? extends ThreatAwareSystem`
|
It is used to fulfill the requirement for system to be able to filter itself based on threat
|
||||||
from `Filtered::by` method. A simple implementation of `ThreatAwareSystem` :
|
properties. The container-like object (`ThreatAwareSystem` in our case) needs to have a method that
|
||||||
|
returns an instance of `Filterer`. This helper interface gives ability to covariantly specify a
|
||||||
|
lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the
|
||||||
|
container-like objects.
|
||||||
|
|
||||||
|
In our example we will be able to pass a predicate that takes `? extends Threat` object and
|
||||||
|
return `? extends ThreatAwareSystem` from `Filtered::by` method. A simple implementation
|
||||||
|
of `ThreatAwareSystem`:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class SimpleThreatAwareSystem implements ThreatAwareSystem {
|
public class SimpleThreatAwareSystem implements ThreatAwareSystem {
|
||||||
|
|
||||||
@ -97,15 +110,21 @@ public class SimpleThreatAwareSystem implements ThreatAwareSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
the `filtered` method is overridden to filter the threats list by given predicate.
|
|
||||||
|
|
||||||
Now if we introduce a new subtype of `Threat` interface that adds probability with which given threat can appear :
|
The `filtered` method is overridden to filter the threats list by given predicate.
|
||||||
|
|
||||||
|
Now if we introduce a new subtype of `Threat` interface that adds probability with which given
|
||||||
|
threat can appear:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface ProbableThreat extends Threat {
|
public interface ProbableThreat extends Threat {
|
||||||
double probability();
|
double probability();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
we can also introduce a new interface that represents a system that is aware of threats with their probabilities :
|
|
||||||
|
We can also introduce a new interface that represents a system that is aware of threats with their
|
||||||
|
probabilities:
|
||||||
|
|
||||||
````java
|
````java
|
||||||
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
|
public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
|
||||||
@Override
|
@Override
|
||||||
@ -115,9 +134,12 @@ public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
|
|||||||
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
|
Filterer<? extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
|
||||||
}
|
}
|
||||||
````
|
````
|
||||||
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify different return covariant type
|
|
||||||
by specifying different generic types. Our interfaces are clean and not cluttered by default implementations. We
|
Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify
|
||||||
we will be able to filter `ProbabilisticThreatAwareSystem` by `ProbableThreat` properties :
|
different return covariant type by specifying different generic types. Our interfaces are clean and
|
||||||
|
not cluttered by default implementations. We we will be able to filter
|
||||||
|
`ProbabilisticThreatAwareSystem` by `ProbableThreat` properties:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
|
public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
|
||||||
|
|
||||||
@ -156,7 +178,8 @@ public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreat
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now if we want filter `ThreatAwareSystem` by threat type we can do :
|
Now if we want filter `ThreatAwareSystem` by threat type we can do:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
|
Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
|
||||||
Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
|
Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
|
||||||
@ -167,7 +190,9 @@ ThreatAwareSystem threatAwareSystem = new SimpleThreatAwareSystem("System-1", th
|
|||||||
ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
|
ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
|
||||||
.by(threat -> threat.type() == ThreatType.ROOTKIT);
|
.by(threat -> threat.type() == ThreatType.ROOTKIT);
|
||||||
```
|
```
|
||||||
or if we want to filter `ProbabilisticThreatAwareSystem` :
|
|
||||||
|
Or if we want to filter `ProbabilisticThreatAwareSystem`:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
|
ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
|
||||||
ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
|
ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
|
||||||
@ -178,27 +203,37 @@ ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem =new SimpleP
|
|||||||
ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
|
ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
|
||||||
.by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
|
.by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
Pattern can be used when working with container-like objects that use subtyping, instead of parametrizing(generics) for extensible class structure.
|
|
||||||
It enables you to easily extend filtering ability of container-like objects as business requirements change.
|
Pattern can be used when working with container-like objects that use subtyping, instead of
|
||||||
|
parametrizing (generics) for extensible class structure. It enables you to easily extend filtering
|
||||||
|
ability of container-like objects as business requirements change.
|
||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
* [Article about Filterer pattern posted on it's author's blog](https://blog.tlinkowski.pl/2018/filterer-pattern/)
|
* [Article about Filterer pattern posted on it's author's blog](https://blog.tlinkowski.pl/2018/filterer-pattern/)
|
||||||
* [Application of Filterer pattern in domain of text analysis](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html)
|
* [Application of Filterer pattern in domain of text analysis](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html)
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
One of the uses is present on the blog presented in [this](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html) link.
|
|
||||||
It presents how to use `Filterer` pattern to create text issue analyzer with support for test cases used for unit testing.
|
One of the uses is present on the blog presented in
|
||||||
|
[this](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html) link. It presents how
|
||||||
|
to use `Filterer` pattern to create text issue analyzer with support for test cases used for unit
|
||||||
|
testing.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
Pros :
|
|
||||||
* you can easily introduce new subtypes for container-like objects and subtypes for objects that are contained within them and still be able to filter easily be new properties of those new subtypes.
|
|
||||||
|
|
||||||
Cons :
|
Pros:
|
||||||
* covariant return types mixed with generics can be sometimes tricky
|
* You can easily introduce new subtypes for container-like objects and subtypes for objects that are contained within them and still be able to filter easily be new properties of those new subtypes.
|
||||||
|
|
||||||
|
Cons:
|
||||||
|
* Covariant return types mixed with generics can be sometimes tricky
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* Author of the pattern : [Tomasz Linkowski](https://tlinkowski.pl/)
|
|
||||||
|
* Author of the pattern : [Tomasz Linkowski](https://tlinkowski.pl/)
|
6
pom.xml
6
pom.xml
@ -52,7 +52,7 @@
|
|||||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||||
<jaxb-impl.version>2.3.2</jaxb-impl.version>
|
<jaxb-impl.version>2.3.2</jaxb-impl.version>
|
||||||
<annotation-api.version>1.3.2</annotation-api.version>
|
<annotation-api.version>1.3.2</annotation-api.version>
|
||||||
<system-rules.version>1.19.0</system-rules.version>
|
<system-lambda.version>1.1.0</system-lambda.version>
|
||||||
<urm.version>2.0.0</urm.version>
|
<urm.version>2.0.0</urm.version>
|
||||||
<mockito-junit-jupiter.version>3.5.0</mockito-junit-jupiter.version>
|
<mockito-junit-jupiter.version>3.5.0</mockito-junit-jupiter.version>
|
||||||
<!-- SonarCloud -->
|
<!-- SonarCloud -->
|
||||||
@ -338,8 +338,8 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.stefanbirkner</groupId>
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
<artifactId>system-rules</artifactId>
|
<artifactId>system-lambda</artifactId>
|
||||||
<version>${system-rules.version}</version>
|
<version>${system-lambda.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.stefanbirkner</groupId>
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
<artifactId>system-rules</artifactId>
|
<artifactId>system-lambda</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
@ -23,55 +23,48 @@
|
|||||||
|
|
||||||
package com.iluwatar.subclasssandbox;
|
package com.iluwatar.subclasssandbox;
|
||||||
|
|
||||||
|
import com.github.stefanbirkner.systemlambda.Statement;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.contrib.java.lang.system.SystemOutRule;
|
|
||||||
|
import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutNormalized;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GroundDive unit tests.
|
* GroundDive unit tests.
|
||||||
*/
|
*/
|
||||||
public class GroundDiveTest {
|
public class GroundDiveTest {
|
||||||
|
|
||||||
@Rule
|
|
||||||
public SystemOutRule log = new SystemOutRule().enableLog();
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMove() {
|
public void testMove() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var groundDive = new GroundDive();
|
var groundDive = new GroundDive();
|
||||||
groundDive.move(1.0, 1.0, 1.0);
|
groundDive.move(1.0, 1.0, 1.0);
|
||||||
var outputLog = getLogContent(log.getLog());
|
var outputLog = getLogContent(() -> groundDive.move(1.0, 1.0, 1.0));
|
||||||
var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
|
var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPlaySound() {
|
public void testPlaySound() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var groundDive = new GroundDive();
|
var groundDive = new GroundDive();
|
||||||
groundDive.playSound("SOUND_NAME", 1);
|
var outputLog = getLogContent(() -> groundDive.playSound("SOUND_NAME", 1));
|
||||||
var outputLog = getLogContent(log.getLog());
|
|
||||||
var expectedLog = "Play SOUND_NAME with volumn 1";
|
var expectedLog = "Play SOUND_NAME with volumn 1";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSpawnParticles() {
|
public void testSpawnParticles() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var groundDive = new GroundDive();
|
var groundDive = new GroundDive();
|
||||||
groundDive.spawnParticles("PARTICLE_TYPE", 100);
|
final var outputLog = getLogContent(
|
||||||
final var outputLog = getLogContent(log.getLog());
|
() -> groundDive.spawnParticles("PARTICLE_TYPE", 100));
|
||||||
final var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
|
final var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testActivate() {
|
public void testActivate() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var groundDive = new GroundDive();
|
var groundDive = new GroundDive();
|
||||||
groundDive.activate();
|
var logs = tapSystemOutNormalized(groundDive::activate)
|
||||||
var logs = log.getLog().split("\n");
|
.split("\n");
|
||||||
final var expectedSize = 3;
|
final var expectedSize = 3;
|
||||||
final var log1 = logs[0].split("-")[1].trim() + " -" + logs[0].split("-")[2].trim();
|
final var log1 = logs[0].split("-")[1].trim() + " -" + logs[0].split("-")[2].trim();
|
||||||
final var expectedLog1 = "Move to ( 0.0, 0.0, -20.0 )";
|
final var expectedLog1 = "Move to ( 0.0, 0.0, -20.0 )";
|
||||||
@ -85,6 +78,11 @@ public class GroundDiveTest {
|
|||||||
Assert.assertEquals(log3, expectedLog3);
|
Assert.assertEquals(log3, expectedLog3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getLogContent(Statement statement) throws Exception {
|
||||||
|
var log = tapSystemOutNormalized(statement);
|
||||||
|
return getLogContent(log);
|
||||||
|
}
|
||||||
|
|
||||||
private String getLogContent(String log) {
|
private String getLogContent(String log) {
|
||||||
return log.split("-")[1].trim();
|
return log.split("-")[1].trim();
|
||||||
}
|
}
|
||||||
|
@ -23,55 +23,47 @@
|
|||||||
|
|
||||||
package com.iluwatar.subclasssandbox;
|
package com.iluwatar.subclasssandbox;
|
||||||
|
|
||||||
|
import com.github.stefanbirkner.systemlambda.Statement;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.contrib.java.lang.system.SystemOutRule;
|
|
||||||
|
import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutNormalized;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SkyLaunch unit tests.
|
* SkyLaunch unit tests.
|
||||||
*/
|
*/
|
||||||
public class SkyLaunchTest {
|
public class SkyLaunchTest {
|
||||||
|
|
||||||
@Rule
|
|
||||||
public SystemOutRule log = new SystemOutRule().enableLog();
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMove() {
|
public void testMove() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var skyLaunch = new SkyLaunch();
|
var skyLaunch = new SkyLaunch();
|
||||||
skyLaunch.move(1.0, 1.0, 1.0);
|
var outputLog = getLogContent(() -> skyLaunch.move(1.0, 1.0, 1.0));
|
||||||
var outputLog = getLogContent(log.getLog());
|
|
||||||
var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
|
var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPlaySound() {
|
public void testPlaySound() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var skyLaunch = new SkyLaunch();
|
var skyLaunch = new SkyLaunch();
|
||||||
skyLaunch.playSound("SOUND_NAME", 1);
|
var outputLog = getLogContent(() -> skyLaunch.playSound("SOUND_NAME", 1));
|
||||||
var outputLog = getLogContent(log.getLog());
|
|
||||||
var expectedLog = "Play SOUND_NAME with volumn 1";
|
var expectedLog = "Play SOUND_NAME with volumn 1";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSpawnParticles() {
|
public void testSpawnParticles() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var skyLaunch = new SkyLaunch();
|
var skyLaunch = new SkyLaunch();
|
||||||
skyLaunch.spawnParticles("PARTICLE_TYPE", 100);
|
var outputLog = getLogContent(
|
||||||
var outputLog = getLogContent(log.getLog());
|
() -> skyLaunch.spawnParticles("PARTICLE_TYPE", 100));
|
||||||
var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
|
var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
|
||||||
Assert.assertEquals(outputLog, expectedLog);
|
Assert.assertEquals(outputLog, expectedLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testActivate() {
|
public void testActivate() throws Exception {
|
||||||
log.clearLog();
|
|
||||||
var skyLaunch = new SkyLaunch();
|
var skyLaunch = new SkyLaunch();
|
||||||
skyLaunch.activate();
|
var logs = tapSystemOutNormalized(skyLaunch::activate)
|
||||||
var logs = log.getLog().split("\n");
|
.split("\n");
|
||||||
final var expectedSize = 3;
|
final var expectedSize = 3;
|
||||||
final var log1 = getLogContent(logs[0]);
|
final var log1 = getLogContent(logs[0]);
|
||||||
final var expectedLog1 = "Move to ( 0.0, 0.0, 20.0 )";
|
final var expectedLog1 = "Move to ( 0.0, 0.0, 20.0 )";
|
||||||
@ -85,6 +77,11 @@ public class SkyLaunchTest {
|
|||||||
Assert.assertEquals(log3, expectedLog3);
|
Assert.assertEquals(log3, expectedLog3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getLogContent(Statement statement) throws Exception {
|
||||||
|
var log = tapSystemOutNormalized(statement);
|
||||||
|
return getLogContent(log);
|
||||||
|
}
|
||||||
|
|
||||||
private String getLogContent(String log) {
|
private String getLogContent(String log) {
|
||||||
return log.split("-")[1].trim();
|
return log.split("-")[1].trim();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user