mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-14 04:16:14 +00:00
docs: update crtp
This commit is contained in:
1 parent
9890579de4
commit
0c31312510
5 files changed
+39
-63
No files matched your search
@@ -3,9 +3,13 @@ title: Curiously Recurring Template Pattern
|
|||||||
language: en
|
language: en
|
||||||
category: Structural
|
category: Structural
|
||||||
tag:
|
tag:
|
||||||
|
- Code simplification
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
- Generic
|
||||||
- Idiom
|
- Idiom
|
||||||
- Instantiation
|
- Instantiation
|
||||||
|
- Polymorphism
|
||||||
|
- Recursion
|
||||||
---
|
---
|
||||||
|
|
||||||
## Also known as
|
## Also known as
|
||||||
@@ -24,7 +28,11 @@ Curiously Recurring Template Pattern (CRTP) is used to achieve a form of static
|
|||||||
|
|
||||||
Real-world example
|
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
|
In plain words
|
||||||
|
|
||||||
@@ -36,7 +44,9 @@ Wikipedia says
|
|||||||
|
|
||||||
**Programmatic example**
|
**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
|
```java
|
||||||
public interface Fighter<T> {
|
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
|
```java
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
|
public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
|
||||||
|
|
||||||
private final String
|
private final String name;
|
||||||
name;
|
private final String surname;
|
||||||
private final String
|
private final String nickName;
|
||||||
surname;
|
private final String speciality;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fight(
|
public void fight(T opponent) {
|
||||||
T opponent) {
|
LOGGER.info("{} is going to fight against {}", this, opponent);
|
||||||
LOGGER.info(
|
|
||||||
"{} is going to fight against {}",
|
|
||||||
this,
|
|
||||||
opponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return
|
|
||||||
name +
|
|
||||||
" \"" +
|
|
||||||
nickName +
|
|
||||||
"\" " +
|
|
||||||
surname;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The followings are some subtypes of MmaFighter.
|
The followings are some subtypes of `MmaFighter`.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
|
class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
|
||||||
@@ -104,7 +83,6 @@ class MmaBantamweightFighter extends MmaFighter<MmaBantamweightFighter> {
|
|||||||
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
|
public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
|
||||||
super(name, surname, nickName, speciality);
|
super(name, surname, nickName, speciality);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MmaHeavyweightFighter extends MmaFighter<MmaHeavyweightFighter> {
|
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) {
|
public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
|
||||||
super(name, surname, nickName, 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.
|
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
|
```java
|
||||||
MmaBantamweightFighter fighter1=new MmaBantamweightFighter("Joe","Johnson","The Geek","Muay Thai");
|
public static void main(String[] args) {
|
||||||
MmaBantamweightFighter fighter2=new MmaBantamweightFighter("Ed","Edwards","The Problem Solver","Judo");
|
|
||||||
fighter1.fight(fighter2); // This is fine
|
|
||||||
|
|
||||||
MmaHeavyweightFighter fighter3=new MmaHeavyweightFighter("Dave","Davidson","The Bug Smasher","Kickboxing");
|
MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
|
||||||
MmaHeavyweightFighter fighter4=new MmaHeavyweightFighter("Jack","Jackson","The Pragmatic","Brazilian Jiu-Jitsu");
|
MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
|
||||||
fighter3.fight(fighter4); // This is fine too
|
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:
|
||||||
|
|
||||||

|
```
|
||||||
|
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
|
## Applicability
|
||||||
|
|
||||||
@@ -145,14 +126,13 @@ MmaBantamweightFighter fighter1=new MmaBantamweightFighter("Joe","Johnson","The
|
|||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
* [The NuaH Blog](https://nuah.livejournal.com/328187.html)
|
* [Curiously Recurring Template Pattern in Java (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)
|
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
|
|
||||||
* Implementing compile-time polymorphic interfaces in template libraries.
|
* 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.
|
* 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
|
## Consequences
|
||||||
|
|
||||||
@@ -176,5 +156,5 @@ Trade-offs:
|
|||||||
|
|
||||||
## Credits
|
## 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)
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||||
* [How do I decrypt "Enum<E extends Enum\<E>>"?](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106)
|
* [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 fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
|
||||||
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
|
MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
|
||||||
fighter3.fight(fighter4);
|
fighter3.fight(fighter4);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,5 +45,4 @@ public class MmaFighter<T extends MmaFighter<T>> implements Fighter<T> {
|
|||||||
public void fight(T opponent) {
|
public void fight(T opponent) {
|
||||||
LOGGER.info("{} is going to fight against {}", this, opponent);
|
LOGGER.info("{} is going to fight against {}", this, opponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user