Skip to content

Commit 700b2be

Browse files
committed
Refactor the prototype pattern to avoid code duplications (Closes iluwatar#584)
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 4cdc309 commit 700b2be

File tree

13 files changed

+37
-69
lines changed

13 files changed

+37
-69
lines changed

lombok.config

Lines changed: 1 addition & 1 deletion
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

Lines changed: 13 additions & 13 deletions
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,28 @@ 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+
public T copy() {
50+
try {
51+
return (T) super.clone();
52+
} catch (CloneNotSupportedException e) {
53+
return null;
54+
}
55+
}
5056
}
5157
```
5258

5359
Our example contains a hierarchy of different creatures. For example, let's look at `Beast` and
5460
`OrcBeast` classes.
5561

5662
```java
57-
@EqualsAndHashCode
63+
@EqualsAndHashCode(callSuper = false)
5864
@NoArgsConstructor
59-
public abstract class Beast implements Prototype {
65+
public abstract class Beast extends Prototype<Beast> {
6066

6167
public Beast(Beast source) {
6268
}
6369

64-
@Override
65-
public abstract Beast copy();
6670
}
6771

6872
@EqualsAndHashCode(callSuper = false)
@@ -76,19 +80,15 @@ public class OrcBeast extends Beast {
7680
this.weapon = orcBeast.weapon;
7781
}
7882

79-
@Override
80-
public OrcBeast copy() {
81-
return new OrcBeast(this);
82-
}
83-
8483
@Override
8584
public String toString() {
8685
return "Orcish wolf attacks with " + weapon;
8786
}
87+
8888
}
8989
```
9090

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

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

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

Lines changed: 2 additions & 5 deletions
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

Lines changed: 0 additions & 5 deletions
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

Lines changed: 0 additions & 5 deletions
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

Lines changed: 4 additions & 11 deletions
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

Lines changed: 2 additions & 5 deletions
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

Lines changed: 0 additions & 5 deletions
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

Lines changed: 0 additions & 5 deletions
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

Lines changed: 0 additions & 5 deletions
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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,17 @@
2626
/**
2727
* Prototype.
2828
*/
29-
public interface Prototype {
30-
31-
Object copy();
29+
public abstract class Prototype<T> implements Cloneable {
3230

31+
/**
32+
* Object a shallow copy of this object or null if this object is not Cloneable.
33+
*/
34+
@SuppressWarnings("unchecked")
35+
public T copy() {
36+
try {
37+
return (T) super.clone();
38+
} catch (CloneNotSupportedException e) {
39+
return null;
40+
}
41+
}
3342
}

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

Lines changed: 2 additions & 5 deletions
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

Lines changed: 1 addition & 1 deletion
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)