Java Pattern Prototype on Game Server
Last Updated :
29 Nov, 2022
Sometimes also known as cloning, basically the prototype is a generative design pattern that allows you to copy objects without making your code dependent on their classes.
Idea
The idea is to create a pack of monsters that will attack our hero/character. A specific configuration is set for enemies – Enemy config. With the help of the prototype, we will be able to copy the basic enemies and make groups of them. We can also copy groups and make up large units of them. We can set parameters, for example, 20 melee enemies and 40 ranged enemies. Based on the basic configuration, we can create copies of the units. That will further allow us to clone entire units. Cool, isn’t it?
Problem
Let’s say you have an Enemy object and you want to create an exact copy of it. How would you do it? First, we need to create a new object of the same class. Then you need to go through all the fields of the original object and copy their values to the new object.
Fine! But there is a catch. Not all objects can be copied in this way because some fields may be closed and not visible from outside the object itself.
There is another problem with the direct approach. Since we need to know the object’s class to create a duplicate, our code becomes dependent on that class. If other addiction doesn’t scare us, there is another snag. Sometimes you only know the interface that an object follows, but not its specific class, when, for example, a parameter in a method accepts any objects that follow some interface.
Solution
The Prototype template delegates the cloning process to natural cloned objects. The template declares a standard interface for all objects that support cloning. This interface allows you to clone an object without binding any code to the class of that object. Usually, such an interface contains only one clone method.
The implementation of the clone method in all classes is very similar. The method creates an object of the current class and transfers all the field values of the old object to the new one. You can even copy private fields because most programming languages allow objects to access the private fields of other objects belonging to the same class.
An object that supports cloning is called a prototype. When your objects have dozens of fields and hundreds of possible configurations, cloning them can be an alternative to subclassing.
Here’s how it works: you create a set of objects that are customizable in various ways. When you need an object like the one you customized, you clone the prototype instead of creating a new object from scratch.
Applicability
Use the prototype pattern when your code does not need to depend on the specific classes of objects that you need to copy.
This often happens when your code works with objects passed to you from third-party code through some interface. The specific classes of these objects are unknown, and you could not depend on them even if you wanted to.
The prototype template provides a common interface for client code to work with all objects that support cloning. This interface makes the client code independent of the specific classes of cloned objects.
The Prototype pattern allows you to use a set of pre-created objects, configured in various ways as prototypes.
Instead of instantiating a subclass to match some configuration, the client can find the appropriate prototype and clone it.
How to implement
Create a prototype interface and declare a clone method in it. Or add a method to all classes of the existing class hierarchy if you have one.

The prototype class must define an alternative constructor that takes an object of that class as an argument. The constructor must copy the values ​​of all fields defined in the class from the passed object to the newly created instance. If you change the subclass, you must call the parent constructor to let the superclass handle cloning of its private fields. If your programming language does not support method overloading, you can define a special method to copy object data. The constructor is a more convenient place because it delivers the resulting object immediately after the call to the new operator.
The cloning method usually consists of one line: starting a new statement with a prototype constructor. Note that each class must explicitly override the clone method and use its class name along with the new operator. Otherwise, the clone method can create an object of the parent class.

Optionally, you can create a centralized prototype registry to store a catalog of frequently used prototypes. Using a static prototype fetch method, you can implement the registry as a new factory class or place it in the prototype base class. This method should search for a prototype based on the search criteria that the client code passes to the method. The requirements can be either a simple string tag or a complex set of search parameters. Once a matching prototype is found, the registry must clone it and return it to the client. Finally, replace direct calls to subclass constructors with calls to the prototype registry factory method.

Copying enemies
Let’s see how you can implement a prototype without the standard Cloneable interface.
Step 1: Let’s create a basic abstract class Enemy
Example 1:
Java
public abstract class Enemy {
public int health;
public int speed;
public String name;
public Enemy() {
}
public Enemy(Enemy target) {
if (target != null ) {
this .health = target.health;
this .speed = target.speed;
this .name = target.name;
}
}
public abstract Enemy clone();
@Override
public boolean equals(Object o) {
if (!(o instanceof Enemy)) return false ;
Enemy enemy = (Enemy) o;
return enemy.health == health && enemy.speed == speed && Objects.equals(enemy.name, name);
}
@Override
public int hashCode() {
return Objects.hash(health, speed, name);
}
}
|
Step 2: We need several enemies. Let it be ArcherEnemy and MeleeEnemy
Example 2-A
Java
public class ArcherEnemy extends Enemy {
public int attackRange;
public ArcherEnemy() {
}
public ArcherEnemy(ArcherEnemy target) {
super (target);
if (target != null ) {
this .attackRange = target.attackRange;
}
}
@Override
public Enemy clone() {
return new ArcherEnemy( this );
}
@Override
public boolean equals(Object o) {
if ( this == o) return true ;
if (o == null || getClass() != o.getClass()) return false ;
if (! super .equals(o)) return false ;
ArcherEnemy that = (ArcherEnemy) o;
return attackRange == that.attackRange ;
}
@Override
public int hashCode() {
return Objects.hash( super .hashCode(), attackRange);
}
}
|
Example 2-B
Java
public class MeleeEnemy extends Enemy {
public int blockChance;
public boolean withShield;
public MeleeEnemy() {}
public MeleeEnemy(MeleeEnemy target)
{
super (target);
if (target != null ) {
this .blockChance = target.blockChance;
this .withShield = target.withShield;
}
}
@Override public Enemy clone()
{
return new MeleeEnemy( this );
}
@Override public boolean equals(Object o)
{
if ( this == o)
return true ;
if (o == null || getClass() != o.getClass())
return false ;
if (! super .equals(o))
return false ;
MeleeEnemy that = (MeleeEnemy)o;
return blockChance == that.blockChance
&& withShield == that.withShield;
}
@Override public int hashCode()
{
return Objects.hash( super .hashCode(), blockChance,
withShield);
}
}
|
Step 3: Let’s test the creation of clones as we have basic enemies ready.
Example 3:
Java
public class Demo {
public static void main(String[] args)
{
List<Enemy> enemyList = new ArrayList<>();
List<Enemy> enemyListCopy = new ArrayList<>();
ArcherEnemy baseArcher = new ArcherEnemy();
baseArcher.health = 150 ;
baseArcher.speed = 35 ;
baseArcher.name = "Base Archer" ;
baseArcher.attackRange = 100 ;
enemyList.add(baseArcher);
ArcherEnemy baseArcherClone
= (ArcherEnemy)baseArcher.clone();
enemyList.add(baseArcherClone);
MeleeEnemy baseMeleeEnemy = new MeleeEnemy();
baseMeleeEnemy.health = 10 ;
baseMeleeEnemy.speed = 20 ;
baseMeleeEnemy.name = "blue" ;
baseMeleeEnemy.blockChance = 7 ;
baseMeleeEnemy.withShield = true ;
enemyList.add(baseMeleeEnemy);
cloneAndCompare(enemyList, enemyListCopy);
}
private static void
cloneAndCompare(List<Enemy> enemyList,
List<Enemy> enemyListCopy)
{
for (Enemy enemy : enemyList) {
enemyListCopy.add(enemy.clone());
}
for ( int i = 0 ; i < enemyList.size(); i++) {
if (enemyList.get(i) != enemyListCopy.get(i)) {
System.out.println(
i
+ ": Enemy are different objects (yay!)" );
if (enemyList.get(i).equals(
enemyListCopy.get(i))) {
System.out.println(
i
+ ": And they are identical (yay!)" );
}
else {
System.out.println(
i
+ ": But they are not identical (booo!)" );
}
}
else {
System.out.println(
i
+ ": Shape objects are the same (booo!)" );
}
}
}
}
|
Output:
0: Enemy are different objects (yay!) // have different links
0: And they are identical (yay!) // but have same inner state
1: Enemy are different objects (yay!) // have different links
1: And they are identical (yay!) // but have same inner state
2: Enemy are different objects (yay!) // have different links
2: And they are identical (yay!) // but have same inner state
We got a list of enemies that we can copy and transfer to the client, if necessary. With this approach, we have the opportunity to use already created enemies and combine them into different groups, storing them in complex data structures.
Share your thoughts in the comments
Please Login to comment...