Skip to content

Commit 11efae3

Browse files
committed
Update explanation for the prototype pattern
1 parent bbc2a9d commit 11efae3

File tree

2 files changed

+117
-25
lines changed

2 files changed

+117
-25
lines changed

prototype/README.md

Lines changed: 116 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ copying this prototype.
1717

1818
## Explanation
1919

20-
First it should be noted that Prototype pattern is not used to gain performance benefits. It's only
21-
used for creating new objects from prototype instance.
20+
First, it should be noted that the Prototype pattern is not used to gain performance benefits. It's only
21+
used for creating new objects from prototype instances.
2222

23-
Real world example
23+
Real-world example
2424

2525
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is
2626
> that it is all about cloning.
2727
2828
In plain words
2929

30-
> Create object based on an existing object through cloning.
30+
> Create an object based on an existing object through cloning.
3131
3232
Wikipedia says
3333

@@ -40,35 +40,127 @@ of going through the trouble of creating an object from scratch and setting it u
4040

4141
**Programmatic Example**
4242

43-
In Java, it can be easily done by implementing `Cloneable` and overriding `clone` from `Object`
43+
In Java, the prototype pattern is recommended to be implemented as follows. First, create an
44+
interface with a method for cloning objects. In this example, `Prototype` interface accomplishes
45+
this with its `copy` method.
4446

4547
```java
46-
class Sheep implements Cloneable {
47-
private String name;
48-
public Sheep(String name) { this.name = name; }
49-
public void setName(String name) { this.name = name; }
50-
public String getName() { return name; }
48+
public interface Prototype {
49+
Object copy();
50+
}
51+
```
52+
53+
Our example contains a hierarchy of different creatures. For example, let's look at `Beast` and
54+
`OrcBeast` classes.
55+
56+
```java
57+
@EqualsAndHashCode
58+
@NoArgsConstructor
59+
public abstract class Beast implements Prototype {
60+
61+
public Beast(Beast source) {
62+
}
63+
64+
@Override
65+
public abstract Beast copy();
66+
}
67+
68+
@EqualsAndHashCode(callSuper = false)
69+
@RequiredArgsConstructor
70+
public class OrcBeast extends Beast {
71+
72+
private final String weapon;
73+
74+
public OrcBeast(OrcBeast orcBeast) {
75+
super(orcBeast);
76+
this.weapon = orcBeast.weapon;
77+
}
78+
79+
@Override
80+
public OrcBeast copy() {
81+
return new OrcBeast(this);
82+
}
83+
5184
@Override
52-
public Sheep clone() {
53-
try {
54-
return (Sheep)super.clone();
55-
} catch(CloneNotSuportedException) {
56-
throw new InternalError();
57-
}
85+
public String toString() {
86+
return "Orcish wolf attacks with " + weapon;
5887
}
5988
}
6089
```
6190

62-
Then it can be cloned like below:
91+
We don't want to go into too much details, but the full example contains also base classes `Mage`
92+
and `Warlord` and there are specialized implementations for those for elves in addition to orcs.
93+
94+
To take full advantage of the prototype pattern, we create `HeroFactory` and `HeroFactoryImpl`
95+
classes to produce different kinds of creatures from prototypes.
96+
97+
```java
98+
public interface HeroFactory {
99+
100+
Mage createMage();
101+
Warlord createWarlord();
102+
Beast createBeast();
103+
}
104+
105+
@RequiredArgsConstructor
106+
public class HeroFactoryImpl implements HeroFactory {
107+
108+
private final Mage mage;
109+
private final Warlord warlord;
110+
private final Beast beast;
111+
112+
public Mage createMage() {
113+
return mage.copy();
114+
}
115+
116+
public Warlord createWarlord() {
117+
return warlord.copy();
118+
}
119+
120+
public Beast createBeast() {
121+
return beast.copy();
122+
}
123+
}
124+
```
125+
126+
Now, we are able to show the full prototype pattern in action producing new creatures by cloning
127+
existing instances.
63128

64129
```java
65-
var original = new Sheep("Jolly");
66-
System.out.println(original.getName()); // Jolly
130+
var factory = new HeroFactoryImpl(
131+
new ElfMage("cooking"),
132+
new ElfWarlord("cleaning"),
133+
new ElfBeast("protecting")
134+
);
135+
var mage = factory.createMage();
136+
var warlord = factory.createWarlord();
137+
var beast = factory.createBeast();
138+
LOGGER.info(mage.toString());
139+
LOGGER.info(warlord.toString());
140+
LOGGER.info(beast.toString());
141+
142+
factory = new HeroFactoryImpl(
143+
new OrcMage("axe"),
144+
new OrcWarlord("sword"),
145+
new OrcBeast("laser")
146+
);
147+
mage = factory.createMage();
148+
warlord = factory.createWarlord();
149+
beast = factory.createBeast();
150+
LOGGER.info(mage.toString());
151+
LOGGER.info(warlord.toString());
152+
LOGGER.info(beast.toString());
153+
```
67154

68-
// Clone and modify what is required
69-
var cloned = original.clone();
70-
cloned.setName("Dolly");
71-
System.out.println(cloned.getName()); // Dolly
155+
Here's the console output from running the example.
156+
157+
```
158+
Elven mage helps in cooking
159+
Elven warlord helps in cleaning
160+
Elven eagle helps in protecting
161+
Orcish mage attacks with axe
162+
Orcish warlord attacks with sword
163+
Orcish wolf attacks with laser
72164
```
73165

74166
## Class diagram
@@ -87,7 +179,7 @@ more convenient to install a corresponding number of prototypes and clone them r
87179
instantiating the class manually, each time with the appropriate state.
88180
* When object creation is expensive compared to cloning.
89181

90-
## Real world examples
182+
## Known uses
91183

92184
* [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29)
93185

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* The Prototype pattern is a creational design pattern in software development. It is used when the
3030
* type of objects to create is determined by a prototypical instance, which is cloned to produce
3131
* new objects. This pattern is used to: - avoid subclasses of an object creator in the client
32-
* application, like the abstract factory pattern does. - avoid the inherent cost of creating a new
32+
* application, like the abstract factory pattern, does. - avoid the inherent cost of creating a new
3333
* object in the standard way (e.g., using the 'new' keyword)
3434
*
3535
* <p>In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by

0 commit comments

Comments
 (0)