Skip to content

Commit 5397f85

Browse files
authored
refactoring: Refactor the prototype pattern to avoid code duplications (Closes iluwatar#584) (iluwatar#1970)
This commit refactors the Prototype pattern by making it Cloneable and thus inheriting the clone() method to its subclasses which removes code duplications.
1 parent c1c8638 commit 5397f85

File tree

13 files changed

+35
-69
lines changed

13 files changed

+35
-69
lines changed

lombok.config

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
lombok.log.fieldName = LOGGER
2-
2+
lombok.addLombokGeneratedAnnotation = true

prototype/README.md

+10-13
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ used for creating new objects from prototype instances.
2222

2323
Real-world example
2424

25-
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is
25+
> Remember Dolly? The sheep that was cloned! Let's not get into the details but the key point here is
2626
> that it is all about cloning.
2727
2828
In plain words
@@ -45,24 +45,25 @@ interface with a method for cloning objects. In this example, `Prototype` interf
4545
this with its `copy` method.
4646

4747
```java
48-
public interface Prototype {
49-
Object copy();
48+
public abstract class Prototype<T> implements Cloneable {
49+
@SneakyThrows
50+
public T copy() {
51+
return (T) super.clone();
52+
}
5053
}
5154
```
5255

5356
Our example contains a hierarchy of different creatures. For example, let's look at `Beast` and
5457
`OrcBeast` classes.
5558

5659
```java
57-
@EqualsAndHashCode
60+
@EqualsAndHashCode(callSuper = false)
5861
@NoArgsConstructor
59-
public abstract class Beast implements Prototype {
62+
public abstract class Beast extends Prototype<Beast> {
6063

6164
public Beast(Beast source) {
6265
}
6366

64-
@Override
65-
public abstract Beast copy();
6667
}
6768

6869
@EqualsAndHashCode(callSuper = false)
@@ -76,19 +77,15 @@ public class OrcBeast extends Beast {
7677
this.weapon = orcBeast.weapon;
7778
}
7879

79-
@Override
80-
public OrcBeast copy() {
81-
return new OrcBeast(this);
82-
}
83-
8480
@Override
8581
public String toString() {
8682
return "Orcish wolf attacks with " + weapon;
8783
}
84+
8885
}
8986
```
9087

91-
We don't want to go into too much details, but the full example contains also base classes `Mage`
88+
We don't want to go into too many details, but the full example contains also base classes `Mage`
9289
and `Warlord` and there are specialized implementations for those for elves in addition to orcs.
9390

9491
To take full advantage of the prototype pattern, we create `HeroFactory` and `HeroFactoryImpl`

prototype/src/main/java/com/iluwatar/prototype/Beast.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@
2929
/**
3030
* Beast.
3131
*/
32-
@EqualsAndHashCode
32+
@EqualsAndHashCode(callSuper = false)
3333
@NoArgsConstructor
34-
public abstract class Beast implements Prototype {
34+
public abstract class Beast extends Prototype<Beast> {
3535

3636
public Beast(Beast source) {
3737
}
3838

39-
@Override
40-
public abstract Beast copy();
41-
4239
}

prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java

-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public ElfBeast(ElfBeast elfBeast) {
4040
this.helpType = elfBeast.helpType;
4141
}
4242

43-
@Override
44-
public ElfBeast copy() {
45-
return new ElfBeast(this);
46-
}
47-
4843
@Override
4944
public String toString() {
5045
return "Elven eagle helps in " + helpType;

prototype/src/main/java/com/iluwatar/prototype/ElfMage.java

-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public ElfMage(ElfMage elfMage) {
4040
this.helpType = elfMage.helpType;
4141
}
4242

43-
@Override
44-
public ElfMage copy() {
45-
return new ElfMage(this);
46-
}
47-
4843
@Override
4944
public String toString() {
5045
return "Elven mage helps in " + helpType;

prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java

+4-11
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,24 @@
2424
package com.iluwatar.prototype;
2525

2626
import lombok.EqualsAndHashCode;
27+
import lombok.RequiredArgsConstructor;
2728

2829
/**
2930
* ElfWarlord.
3031
*/
31-
@EqualsAndHashCode
32+
@EqualsAndHashCode(callSuper = true)
33+
@RequiredArgsConstructor
3234
public class ElfWarlord extends Warlord {
3335

3436
private final String helpType;
3537

36-
public ElfWarlord(String helpType) {
37-
this.helpType = helpType;
38-
}
39-
4038
public ElfWarlord(ElfWarlord elfWarlord) {
4139
super(elfWarlord);
4240
this.helpType = elfWarlord.helpType;
4341
}
4442

45-
@Override
46-
public ElfWarlord copy() {
47-
return new ElfWarlord(this);
48-
}
49-
5043
@Override
5144
public String toString() {
5245
return "Elven warlord helps in " + helpType;
5346
}
54-
}
47+
}

prototype/src/main/java/com/iluwatar/prototype/Mage.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@
2929
/**
3030
* Mage.
3131
*/
32-
@EqualsAndHashCode
32+
@EqualsAndHashCode(callSuper = false)
3333
@NoArgsConstructor
34-
public abstract class Mage implements Prototype {
34+
public abstract class Mage extends Prototype<Mage> {
3535

3636
public Mage(Mage source) {
3737
}
3838

39-
@Override
40-
public abstract Mage copy();
41-
4239
}

prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java

-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public OrcBeast(OrcBeast orcBeast) {
4040
this.weapon = orcBeast.weapon;
4141
}
4242

43-
@Override
44-
public OrcBeast copy() {
45-
return new OrcBeast(this);
46-
}
47-
4843
@Override
4944
public String toString() {
5045
return "Orcish wolf attacks with " + weapon;

prototype/src/main/java/com/iluwatar/prototype/OrcMage.java

-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public OrcMage(OrcMage orcMage) {
4040
this.weapon = orcMage.weapon;
4141
}
4242

43-
@Override
44-
public OrcMage copy() {
45-
return new OrcMage(this);
46-
}
47-
4843
@Override
4944
public String toString() {
5045
return "Orcish mage attacks with " + weapon;

prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java

-5
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ public OrcWarlord(OrcWarlord orcWarlord) {
4040
this.weapon = orcWarlord.weapon;
4141
}
4242

43-
@Override
44-
public OrcWarlord copy() {
45-
return new OrcWarlord(this);
46-
}
47-
4843
@Override
4944
public String toString() {
5045
return "Orcish warlord attacks with " + weapon;

prototype/src/main/java/com/iluwatar/prototype/Prototype.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,21 @@
2323

2424
package com.iluwatar.prototype;
2525

26+
import lombok.SneakyThrows;
27+
import lombok.extern.slf4j.Slf4j;
28+
2629
/**
2730
* Prototype.
2831
*/
29-
public interface Prototype {
30-
31-
Object copy();
32+
@Slf4j
33+
public abstract class Prototype<T> implements Cloneable {
3234

35+
/**
36+
* Object a shallow copy of this object or null if this object is not Cloneable.
37+
*/
38+
@SuppressWarnings("unchecked")
39+
@SneakyThrows
40+
public T copy() {
41+
return (T) super.clone();
42+
}
3343
}

prototype/src/main/java/com/iluwatar/prototype/Warlord.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@
2929
/**
3030
* Warlord.
3131
*/
32-
@EqualsAndHashCode
32+
@EqualsAndHashCode(callSuper = false)
3333
@NoArgsConstructor
34-
public abstract class Warlord implements Prototype {
34+
public abstract class Warlord extends Prototype<Warlord> {
3535

3636
public Warlord(Warlord source) {
3737
}
3838

39-
@Override
40-
public abstract Warlord copy();
41-
4239
}

prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
* @param <P> Prototype
4040
* @author Jeroen Meulemeester
4141
*/
42-
class PrototypeTest<P extends Prototype> {
42+
class PrototypeTest<P extends Prototype<P>> {
4343
static Collection<Object[]> dataProvider() {
4444
return List.of(
4545
new Object[]{new OrcBeast("axe"), "Orcish wolf attacks with axe"},

0 commit comments

Comments
 (0)