docs: update crtp

This commit is contained in:
Ilkka Seppälä
2024-05-25 08:52:39 +03:00
parent 9890579de4
commit 0c31312510
5 changed files with 39 additions and 63 deletions
+39 -59
View File
@@ -3,9 +3,13 @@ title: Curiously Recurring Template Pattern
language: en
category: Structural
tag:
- Code simplification
- Extensibility
- Generic
- Idiom
- Instantiation
- Polymorphism
- Recursion
---
## Also known as
@@ -24,7 +28,11 @@ Curiously Recurring Template Pattern (CRTP) is used to achieve a form of static
Real-world example
> For a mixed martial arts promotion that is planning an event, ensuring that the fights are organized between athletes of the same weight class is crucial. This prevents mismatches between fighters of significantly different sizes, such as a heavyweight facing off against a bantamweight.
> Consider a scenario where a library system manages various types of media: books, DVDs, and magazines. Each media type has specific attributes and behaviors, but they all share common functionality like borrowing and returning.
>
> Using the Curiously Recurring Template Pattern (CRTP), you can create a base template class `MediaItem` that includes these common methods. Each specific media type (e.g., `Book`, `DVD`, `Magazine`) would then inherit from `MediaItem` using itself as a template parameter. This allows each media type to customize the common functionality without the overhead of virtual methods.
>
> For example, `Book` would inherit from `MediaItem<Book>`, allowing the library system to use polymorphic behavior at compile-time, ensuring that each media type implements the necessary methods efficiently. This approach provides the benefits of polymorphism and code reuse while maintaining high performance and type safety.
In plain words
@@ -36,7 +44,9 @@ Wikipedia says
**Programmatic example**
Let's define the generic interface Fighter.
For a mixed martial arts promotion that is planning an event, ensuring that the fights are organized between athletes of the same weight class is crucial. This prevents mismatches between fighters of significantly different sizes, such as a heavyweight facing off against a bantamweight.
Let's define the generic interface `Fighter`.
```java
public interface Fighter<T> {
@@ -46,57 +56,26 @@ public interface Fighter<T> {
}
```
The MMAFighter class is used to instantiate fighters distinguished by their weight class.
The `MMAFighter` class is used to instantiate fighters distinguished by their weight class.
```java
@Slf4j
@Data
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
private final String
name;
private final String
surname;
private final String
nickName;
private final String
speciality;
public MmaFighter(
String name,
String surname,
String nickName,
String speciality) {
this.name =
name;
this.surname =
surname;
this.nickName =
nickName;
this.speciality =
speciality;
}
private final String name;
private final String surname;
private final String nickName;
private final String speciality;
@Override
public void fight(
T opponent) {
LOGGER.info(
"{} is going to fight against {}",
this,
opponent);
}
@Override
public String toString() {
return
name +
" \"" +
nickName +
"\" " +
surname;
public void fight(T opponent) {
LOGGER.info("{} is going to fight against {}", this, opponent);
}
}
```
The followings are some subtypes of MmaFighter.
The followings are some subtypes of `MmaFighter`.
```java
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
@@ -104,7 +83,6 @@ class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
super(name, surname, nickName, speciality);
}
}
public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
@@ -112,27 +90,30 @@ public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
super(name, surname, nickName, speciality);
}
}
```
A fighter is allowed to fight an opponent of the same weight classes. If the opponent is of a different weight class, an error is raised.
```java
MmaBantamweightFighter fighter1=new MmaBantamweightFighter("Joe","Johnson","The Geek","Muay Thai");
MmaBantamweightFighter fighter2=new MmaBantamweightFighter("Ed","Edwards","The Problem Solver","Judo");
fighter1.fight(fighter2); // This is fine
public static void main(String[] args) {
MmaHeavyweightFighter fighter3=new MmaHeavyweightFighter("Dave","Davidson","The Bug Smasher","Kickboxing");
MmaHeavyweightFighter fighter4=new MmaHeavyweightFighter("Jack","Jackson","The Pragmatic","Brazilian Jiu-Jitsu");
fighter3.fight(fighter4); // This is fine too
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
fighter1.fight(fighter2);
fighter1.fight(fighter3); // This will raise a compilation error
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
fighter3.fight(fighter4);
}
```
## Class diagram
Program output:
![alt text](./etc/crtp.png "CRTP class diagram")
```
08:42:34.048 [main] INFO crtp.MmaFighter -- MmaFighter(name=Joe, surname=Johnson, nickName=The Geek, speciality=Muay Thai) is going to fight against MmaFighter(name=Ed, surname=Edwards, nickName=The Problem Solver, speciality=Judo)
08:42:34.054 [main] INFO crtp.MmaFighter -- MmaFighter(name=Dave, surname=Davidson, nickName=The Bug Smasher, speciality=Kickboxing) is going to fight against MmaFighter(name=Jack, surname=Jackson, nickName=The Pragmatic, speciality=Brazilian Jiu-Jitsu)
```
## Applicability
@@ -145,14 +126,13 @@ MmaBantamweightFighter fighter1=new MmaBantamweightFighter("Joe","Johnson","The
## Tutorials
* [The NuaH Blog](https://nuah.livejournal.com/328187.html)
* Yogesh Umesh Vaity's answer to [What does "Recursive type bound" in Generics mean?](https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean)
* [Curiously Recurring Template Pattern in Java (The NuaH Blog)](https://nuah.livejournal.com/328187.html)
## Known uses
* Implementing compile-time polymorphic interfaces in template libraries.
* Enhancing code reuse in libraries where performance is critical, like in mathematical computations, embedded systems, and real-time processing applications.
* [java.lang.Enum](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Enum.html)
* Implementation of the `Cloneable` interface in various Java libraries.
## Consequences
@@ -176,5 +156,5 @@ Trade-offs:
## Credits
* [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)
* [How do I decrypt "Enum<E extends Enum\<E>>"?](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106)
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
* [Effective Java](https://amzn.to/4cGk2Jz)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

@@ -1,2 +0,0 @@
@startuml
@enduml
@@ -47,6 +47,5 @@ public class App {
MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
fighter3.fight(fighter4);
}
}
@@ -45,5 +45,4 @@ public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
public void fight(T opponent) {
LOGGER.info("{} is going to fight against {}", this, opponent);
}
}