Skip to content

Commit 753587d

Browse files
AddeusExMachinaAntonio Addeo
and
Antonio Addeo
authored
feat: Add CRTP pattern Ref: #2342 (#2580)
* Add crtp directory with builder for object hierarchy example Ref: #2342 * Add space package Ref: #2342 * Add comments for checkstyle Ref: #2342 * Add fight package as second crtp example Ref: #2342 * Add README.md Ref: #2342 * Add use cases examples in README.md for crtp Ref: #2342 * Remove space package Ref: #2342 * Move files from fight package to crtp Ref: #2342 * Add real world example in README.md Ref: #2342 * Update crtp README.md Ref: #2342 * Delete FighterTest Ref: #2342 * Update App and AppTest in crtp Ref: #2342 * Add category and tag in README.md frontmatter Ref: #2342 * Add @DaTa annotation and remove constructor and toString in MmaFighter Ref: #2342 * Add FightTest Ref: #2342 --------- Co-authored-by: Antonio Addeo <[email protected]>
1 parent 5891f29 commit 753587d

11 files changed

+537
-0
lines changed

Diff for: crtp/README.md

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
title: Curiously Recurring Template Pattern
3+
language: en
4+
category: Structural
5+
tag:
6+
- Extensibility
7+
- Instantiation
8+
---
9+
10+
## Name / classification
11+
12+
Curiously Recurring Template Pattern
13+
14+
## Also known as
15+
16+
Recursive Type Bound, Recursive Generic
17+
18+
## Intent
19+
20+
Allow derived components to inherit certain functionalities from a base component that are compatible with the derived type.
21+
22+
## Explanation
23+
24+
Real-world example
25+
26+
> For a mixed martial arts promotion that is planning an event, ensuring that the fights are organized between athletes
27+
> of the same weight class is crucial. This prevents mismatches between fighters of significantly different sizes, such
28+
> as a heavyweight facing off against a bantamweight.
29+
30+
In plain words
31+
32+
> Make certain methods within a type to accept arguments specific to its subtypes.
33+
34+
Wikipedia says
35+
36+
> The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X
37+
> derives from a class template instantiation using X itself as a template argument.
38+
39+
**Programmatic example**
40+
41+
Let's define the generic interface Fighter
42+
43+
```java
44+
public interface Fighter<T> {
45+
46+
void fight(T t);
47+
48+
}
49+
```
50+
51+
The MMAFighter class is used to instantiate fighters distinguished by their weight class
52+
53+
```java
54+
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
55+
56+
private final String name;
57+
private final String surname;
58+
private final String nickName;
59+
private final String speciality;
60+
61+
public MmaFighter(String name, String surname, String nickName, String speciality) {
62+
this.name = name;
63+
this.surname = surname;
64+
this.nickName = nickName;
65+
this.speciality = speciality;
66+
}
67+
68+
@Override
69+
public void fight(T opponent) {
70+
LOGGER.info("{} is going to fight against {}", this, opponent);
71+
}
72+
73+
@Override
74+
public String toString() {
75+
return name + " \"" + nickName + "\" " + surname;
76+
}
77+
```
78+
79+
The followings are some subtypes of MmaFighter
80+
81+
```java
82+
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
83+
84+
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
85+
super(name, surname, nickName, speciality);
86+
}
87+
88+
}
89+
90+
public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
91+
92+
public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
93+
super(name, surname, nickName, speciality);
94+
}
95+
96+
}
97+
```
98+
99+
A fighter is allowed to fight an opponent of the same weight classes, if the opponent is of a different weight class
100+
there is an error
101+
102+
```java
103+
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
104+
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
105+
fighter1.fight(fighter2); // This is fine
106+
107+
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
108+
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
109+
fighter3.fight(fighter4); // This is fine too
110+
111+
fighter1.fight(fighter3); // This will raise a compilation error
112+
```
113+
114+
## Class diagram
115+
116+
![alt text](./etc/crtp.png "CRTP class diagram")
117+
118+
## Applicability
119+
120+
Use the Curiously Recurring Template Pattern when
121+
122+
* You have type conflicts when chaining methods in an object hierarchy
123+
* You want to use a parameterized class method that can accept subclasses of the class as arguments, allowing it to be applied to objects that inherit from the class
124+
* You want certain methods to work only with instances of the same type, such as for achieving mutual comparability.
125+
126+
## Tutorials
127+
128+
* [The NuaH Blog](https://nuah.livejournal.com/328187.html)
129+
* Yogesh Umesh Vaity answer to [What does "Recursive type bound" in Generics mean?](https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean)
130+
131+
## Known uses
132+
133+
* [java.lang.Enum](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html)
134+
135+
## Credits
136+
137+
* [How do I decrypt "Enum<E extends Enum<E>>"?](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106)
138+
* Chapter 5 Generics, Item 30 in [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb)

Diff for: crtp/etc/crtp.png

120 KB
Loading

Diff for: crtp/pom.xml

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
5+
6+
The MIT License
7+
Copyright © 2014-2022 Ilkka Seppälä
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
-->
28+
<project xmlns="http://maven.apache.org/POM/4.0.0"
29+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
31+
<modelVersion>4.0.0</modelVersion>
32+
<parent>
33+
<groupId>com.iluwatar</groupId>
34+
<artifactId>java-design-patterns</artifactId>
35+
<version>1.26.0-SNAPSHOT</version>
36+
</parent>
37+
<artifactId>crtp</artifactId>
38+
<dependencies>
39+
<dependency>
40+
<groupId>org.junit.jupiter</groupId>
41+
<artifactId>junit-jupiter-engine</artifactId>
42+
<scope>test</scope>
43+
</dependency>
44+
</dependencies>
45+
<build>
46+
<plugins>
47+
<plugin>
48+
<groupId>org.apache.maven.plugins</groupId>
49+
<artifactId>maven-assembly-plugin</artifactId>
50+
<executions>
51+
<execution>
52+
<configuration>
53+
<archive>
54+
<manifest>
55+
<mainClass>com.iluwatar.crtp.App</mainClass>
56+
</manifest>
57+
</archive>
58+
</configuration>
59+
</execution>
60+
</executions>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
</project>

Diff for: crtp/src/main/java/crtp/App.java

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package crtp;
26+
27+
import lombok.extern.slf4j.Slf4j;
28+
29+
/**
30+
* Shows the {@link Fighter} fight method call on some implementations of {@link MmaFighter}. Note
31+
* that fighters can only fight against opponents of their same weight class.
32+
*/
33+
@Slf4j
34+
public class App {
35+
36+
/**
37+
* Program entry point.
38+
*
39+
* @param args command line args
40+
*/
41+
public static void main(String[] args) {
42+
43+
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
44+
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
45+
fighter1.fight(fighter2);
46+
47+
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
48+
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
49+
fighter3.fight(fighter4);
50+
51+
}
52+
}

Diff for: crtp/src/main/java/crtp/Fighter.java

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package crtp;
26+
27+
/**
28+
* Fighter interface.
29+
*
30+
* @param <T> The type of fighter.
31+
*/
32+
public interface Fighter<T> {
33+
34+
void fight(T t);
35+
36+
}

Diff for: crtp/src/main/java/crtp/MmaBantamweightFighter.java

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package crtp;
26+
27+
/**
28+
* MmaBantamweightFighter class.
29+
*/
30+
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
31+
32+
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
33+
super(name, surname, nickName, speciality);
34+
}
35+
36+
}

Diff for: crtp/src/main/java/crtp/MmaFighter.java

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
3+
*
4+
* The MIT License
5+
* Copyright © 2014-2022 Ilkka Seppälä
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package crtp;
26+
27+
import lombok.Data;
28+
import lombok.extern.slf4j.Slf4j;
29+
30+
/**
31+
* MmaFighter class.
32+
*
33+
* @param <T> MmaFighter derived class that uses itself as type parameter.
34+
*/
35+
@Slf4j
36+
@Data
37+
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
38+
39+
private final String name;
40+
private final String surname;
41+
private final String nickName;
42+
private final String speciality;
43+
44+
@Override
45+
public void fight(T opponent) {
46+
LOGGER.info("{} is going to fight against {}", this, opponent);
47+
}
48+
49+
}

0 commit comments

Comments
 (0)