refactoring: Refactor the prototype pattern to avoid code duplications (Closes #584) (#1970)

This commit  refactors the Prototype pattern by making it Cloneable and thus inheriting the clone() method to its subclasses which removes code duplications.
This commit is contained in:
Yonatan Karp-Rudin
2022-09-08 14:55:02 +02:00
committed by GitHub
parent c1c863843f
commit 5397f85d7d
13 changed files with 35 additions and 69 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
lombok.log.fieldName = LOGGER
lombok.addLombokGeneratedAnnotation = true
+10 -13
View File
@@ -22,7 +22,7 @@ used for creating new objects from prototype instances.
Real-world example
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is
> Remember Dolly? The sheep that was cloned! Let's not get into the details but the key point here is
> that it is all about cloning.
In plain words
@@ -45,8 +45,11 @@ interface with a method for cloning objects. In this example, `Prototype` interf
this with its `copy` method.
```java
public interface Prototype {
Object copy();
public abstract class Prototype<T> implements Cloneable {
@SneakyThrows
public T copy() {
return (T) super.clone();
}
}
```
@@ -54,15 +57,13 @@ Our example contains a hierarchy of different creatures. For example, let's look
`OrcBeast` classes.
```java
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Beast implements Prototype {
public abstract class Beast extends Prototype<Beast> {
public Beast(Beast source) {
}
@Override
public abstract Beast copy();
}
@EqualsAndHashCode(callSuper = false)
@@ -76,19 +77,15 @@ public class OrcBeast extends Beast {
this.weapon = orcBeast.weapon;
}
@Override
public OrcBeast copy() {
return new OrcBeast(this);
}
@Override
public String toString() {
return "Orcish wolf attacks with " + weapon;
}
}
```
We don't want to go into too much details, but the full example contains also base classes `Mage`
We don't want to go into too many details, but the full example contains also base classes `Mage`
and `Warlord` and there are specialized implementations for those for elves in addition to orcs.
To take full advantage of the prototype pattern, we create `HeroFactory` and `HeroFactoryImpl`
@@ -29,14 +29,11 @@ import lombok.NoArgsConstructor;
/**
* Beast.
*/
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Beast implements Prototype {
public abstract class Beast extends Prototype<Beast> {
public Beast(Beast source) {
}
@Override
public abstract Beast copy();
}
@@ -40,11 +40,6 @@ public class ElfBeast extends Beast {
this.helpType = elfBeast.helpType;
}
@Override
public ElfBeast copy() {
return new ElfBeast(this);
}
@Override
public String toString() {
return "Elven eagle helps in " + helpType;
@@ -40,11 +40,6 @@ public class ElfMage extends Mage {
this.helpType = elfMage.helpType;
}
@Override
public ElfMage copy() {
return new ElfMage(this);
}
@Override
public String toString() {
return "Elven mage helps in " + helpType;
@@ -24,31 +24,24 @@
package com.iluwatar.prototype;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
/**
* ElfWarlord.
*/
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = true)
@RequiredArgsConstructor
public class ElfWarlord extends Warlord {
private final String helpType;
public ElfWarlord(String helpType) {
this.helpType = helpType;
}
public ElfWarlord(ElfWarlord elfWarlord) {
super(elfWarlord);
this.helpType = elfWarlord.helpType;
}
@Override
public ElfWarlord copy() {
return new ElfWarlord(this);
}
@Override
public String toString() {
return "Elven warlord helps in " + helpType;
}
}
}
@@ -29,14 +29,11 @@ import lombok.NoArgsConstructor;
/**
* Mage.
*/
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Mage implements Prototype {
public abstract class Mage extends Prototype<Mage> {
public Mage(Mage source) {
}
@Override
public abstract Mage copy();
}
@@ -40,11 +40,6 @@ public class OrcBeast extends Beast {
this.weapon = orcBeast.weapon;
}
@Override
public OrcBeast copy() {
return new OrcBeast(this);
}
@Override
public String toString() {
return "Orcish wolf attacks with " + weapon;
@@ -40,11 +40,6 @@ public class OrcMage extends Mage {
this.weapon = orcMage.weapon;
}
@Override
public OrcMage copy() {
return new OrcMage(this);
}
@Override
public String toString() {
return "Orcish mage attacks with " + weapon;
@@ -40,11 +40,6 @@ public class OrcWarlord extends Warlord {
this.weapon = orcWarlord.weapon;
}
@Override
public OrcWarlord copy() {
return new OrcWarlord(this);
}
@Override
public String toString() {
return "Orcish warlord attacks with " + weapon;
@@ -23,11 +23,21 @@
package com.iluwatar.prototype;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* Prototype.
*/
public interface Prototype {
Object copy();
@Slf4j
public abstract class Prototype<T> implements Cloneable {
/**
* Object a shallow copy of this object or null if this object is not Cloneable.
*/
@SuppressWarnings("unchecked")
@SneakyThrows
public T copy() {
return (T) super.clone();
}
}
@@ -29,14 +29,11 @@ import lombok.NoArgsConstructor;
/**
* Warlord.
*/
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Warlord implements Prototype {
public abstract class Warlord extends Prototype<Warlord> {
public Warlord(Warlord source) {
}
@Override
public abstract Warlord copy();
}
@@ -39,7 +39,7 @@ import org.junit.jupiter.params.provider.MethodSource;
* @param <P> Prototype
* @author Jeroen Meulemeester
*/
class PrototypeTest<P extends Prototype> {
class PrototypeTest<P extends Prototype<P>> {
static Collection<Object[]> dataProvider() {
return List.of(
new Object[]{new OrcBeast("axe"), "Orcish wolf attacks with axe"},