Update explanation for the prototype pattern
This commit is contained in:
parent
bbc2a9ddb1
commit
11efae38f2
@ -17,17 +17,17 @@ copying this prototype.
|
|||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
First it should be noted that Prototype pattern is not used to gain performance benefits. It's only
|
First, it should be noted that the Prototype pattern is not used to gain performance benefits. It's only
|
||||||
used for creating new objects from prototype instance.
|
used for creating new objects from prototype instances.
|
||||||
|
|
||||||
Real world example
|
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! Lets not get into the details but the key point here is
|
||||||
> that it is all about cloning.
|
> that it is all about cloning.
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
> Create object based on an existing object through cloning.
|
> Create an object based on an existing object through cloning.
|
||||||
|
|
||||||
Wikipedia says
|
Wikipedia says
|
||||||
|
|
||||||
@ -40,35 +40,127 @@ of going through the trouble of creating an object from scratch and setting it u
|
|||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
In Java, it can be easily done by implementing `Cloneable` and overriding `clone` from `Object`
|
In Java, the prototype pattern is recommended to be implemented as follows. First, create an
|
||||||
|
interface with a method for cloning objects. In this example, `Prototype` interface accomplishes
|
||||||
|
this with its `copy` method.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
class Sheep implements Cloneable {
|
public interface Prototype {
|
||||||
private String name;
|
Object copy();
|
||||||
public Sheep(String name) { this.name = name; }
|
}
|
||||||
public void setName(String name) { this.name = name; }
|
```
|
||||||
public String getName() { return name; }
|
|
||||||
|
Our example contains a hierarchy of different creatures. For example, let's look at `Beast` and
|
||||||
|
`OrcBeast` classes.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@NoArgsConstructor
|
||||||
|
public abstract class Beast implements Prototype {
|
||||||
|
|
||||||
|
public Beast(Beast source) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sheep clone() {
|
public abstract Beast copy();
|
||||||
try {
|
}
|
||||||
return (Sheep)super.clone();
|
|
||||||
} catch(CloneNotSuportedException) {
|
@EqualsAndHashCode(callSuper = false)
|
||||||
throw new InternalError();
|
@RequiredArgsConstructor
|
||||||
}
|
public class OrcBeast extends Beast {
|
||||||
|
|
||||||
|
private final String weapon;
|
||||||
|
|
||||||
|
public OrcBeast(OrcBeast orcBeast) {
|
||||||
|
super(orcBeast);
|
||||||
|
this.weapon = orcBeast.weapon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OrcBeast copy() {
|
||||||
|
return new OrcBeast(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Orcish wolf attacks with " + weapon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then it can be cloned like below:
|
We don't want to go into too much 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`
|
||||||
|
classes to produce different kinds of creatures from prototypes.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var original = new Sheep("Jolly");
|
public interface HeroFactory {
|
||||||
System.out.println(original.getName()); // Jolly
|
|
||||||
|
Mage createMage();
|
||||||
|
Warlord createWarlord();
|
||||||
|
Beast createBeast();
|
||||||
|
}
|
||||||
|
|
||||||
// Clone and modify what is required
|
@RequiredArgsConstructor
|
||||||
var cloned = original.clone();
|
public class HeroFactoryImpl implements HeroFactory {
|
||||||
cloned.setName("Dolly");
|
|
||||||
System.out.println(cloned.getName()); // Dolly
|
private final Mage mage;
|
||||||
|
private final Warlord warlord;
|
||||||
|
private final Beast beast;
|
||||||
|
|
||||||
|
public Mage createMage() {
|
||||||
|
return mage.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Warlord createWarlord() {
|
||||||
|
return warlord.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Beast createBeast() {
|
||||||
|
return beast.copy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, we are able to show the full prototype pattern in action producing new creatures by cloning
|
||||||
|
existing instances.
|
||||||
|
|
||||||
|
```java
|
||||||
|
var factory = new HeroFactoryImpl(
|
||||||
|
new ElfMage("cooking"),
|
||||||
|
new ElfWarlord("cleaning"),
|
||||||
|
new ElfBeast("protecting")
|
||||||
|
);
|
||||||
|
var mage = factory.createMage();
|
||||||
|
var warlord = factory.createWarlord();
|
||||||
|
var beast = factory.createBeast();
|
||||||
|
LOGGER.info(mage.toString());
|
||||||
|
LOGGER.info(warlord.toString());
|
||||||
|
LOGGER.info(beast.toString());
|
||||||
|
|
||||||
|
factory = new HeroFactoryImpl(
|
||||||
|
new OrcMage("axe"),
|
||||||
|
new OrcWarlord("sword"),
|
||||||
|
new OrcBeast("laser")
|
||||||
|
);
|
||||||
|
mage = factory.createMage();
|
||||||
|
warlord = factory.createWarlord();
|
||||||
|
beast = factory.createBeast();
|
||||||
|
LOGGER.info(mage.toString());
|
||||||
|
LOGGER.info(warlord.toString());
|
||||||
|
LOGGER.info(beast.toString());
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's the console output from running the example.
|
||||||
|
|
||||||
|
```
|
||||||
|
Elven mage helps in cooking
|
||||||
|
Elven warlord helps in cleaning
|
||||||
|
Elven eagle helps in protecting
|
||||||
|
Orcish mage attacks with axe
|
||||||
|
Orcish warlord attacks with sword
|
||||||
|
Orcish wolf attacks with laser
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
@ -87,7 +179,7 @@ more convenient to install a corresponding number of prototypes and clone them r
|
|||||||
instantiating the class manually, each time with the appropriate state.
|
instantiating the class manually, each time with the appropriate state.
|
||||||
* When object creation is expensive compared to cloning.
|
* When object creation is expensive compared to cloning.
|
||||||
|
|
||||||
## Real world examples
|
## Known uses
|
||||||
|
|
||||||
* [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29)
|
* [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29)
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* The Prototype pattern is a creational design pattern in software development. It is used when the
|
* The Prototype pattern is a creational design pattern in software development. It is used when the
|
||||||
* type of objects to create is determined by a prototypical instance, which is cloned to produce
|
* type of objects to create is determined by a prototypical instance, which is cloned to produce
|
||||||
* new objects. This pattern is used to: - avoid subclasses of an object creator in the client
|
* new objects. This pattern is used to: - avoid subclasses of an object creator in the client
|
||||||
* application, like the abstract factory pattern does. - avoid the inherent cost of creating a new
|
* application, like the abstract factory pattern, does. - avoid the inherent cost of creating a new
|
||||||
* object in the standard way (e.g., using the 'new' keyword)
|
* object in the standard way (e.g., using the 'new' keyword)
|
||||||
*
|
*
|
||||||
* <p>In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by
|
* <p>In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by
|
||||||
|
Loading…
x
Reference in New Issue
Block a user