Learning Design Mode from Zero (5) - Prototype Pattern

Keywords: Java Database

Prototype mode

The difficulty factor for this model is elementary and proposed by Gang Of Four.

Prototype mode is used to create duplicate objects and improve performance.This pattern implements a prototype interface for creating a clone of the current object.This pattern is used when creating objects directly is expensive.

For example, an object needs to be created after a costly database operation or remote connection. We can cache the object, return its clone on the next request, update the database when needed, or notify the remote connection to reduce calls to the database or remote connection.

intention

Specify the kind of objects created with prototype instances, and create new objects by copying them.

Main Solution: Create and delete prototypes at runtime.

When to use:
1. When a system should be created, composed and represented independently of its products.
2. When the class to be instantiated is specified at run time, for example, by dynamic loading.
3. To avoid creating a factory class hierarchy parallel to the product class hierarchy.
4. When an instance of a class can only have one of several different combinations of states.It may be easier to prototype and clone a corresponding number of them than to manually instantiate the class each time in the appropriate state.

How to solve this problem: Using an existing prototype object, quickly generate the same instance as the prototype object.

Key Code:
1. Implement clone operation, implement Cloneable interface in JAVA, override clone() method.In.NET, you can use the MemberwiseClone() method of the Object class to make a shallow copy of an object or a deep copy by serializing it.
2. The prototype pattern is also used to isolate the coupling between the user of the class object and the specific type (volatile class), which also requires that these "volatile classes" have a stable interface.

explain

Examples of the real world

Remember Dolly?Cloned sheep!Let's not go into details, the key point here is all cloning

In short

Create new objects by cloning based on existing objects

Wikipedia

Prototype mode is a creative design mode in software development.When the type of object to be created is determined and used by a prototype instance, the prototype instance is cloned to produce a new object.

In short, it allows you to create a copy of an existing object and modify it to suit your needs instead of creating an object from scratch and setting it up

Program example

Cloning objects is easy in Java by implementing the java.lang.Cloneable interface and overriding the clone method

class Sheep implements Cloneable {
  private String name;
  public Sheep(String name) { this.name = name; }
  public void setName(String name) { this.name = name; }
  public String getName() { return name; }
  @Override
  public Sheep clone() throws CloneNotSupportedException {
    return new Sheep(name);
  }
}

The Sheep class object above can then be cloned as follows:

Sheep original = new Sheep("Jolly");
System.out.println(original.getName()); // Jolly

// Clone and modify what is required
Sheep cloned = original.clone();
cloned.setName("Dolly");
System.out.println(cloned.getName()); // Dolly

Application Scenarios

1. Resource optimization scenarios.
2. Class initialization needs to digest a lot of resources, including data, hardware resources, etc.
3. Scenarios for performance and security requirements.
4. If generating an object from new requires very tedious data preparation or access rights, you can use the prototype mode.
5. Scenarios for multiple modifiers of an object.
6. When an object needs to be accessed by other objects and each caller may need to modify its value, consider using prototype mode to copy multiple objects for the caller to use

Realistic examples in Java

Write at the end

Note: Unlike constructing new objects by instantiating a class, a prototype pattern generates new objects by copying an existing object.
Shallow copy implements Cloneable interface, overrides clone method, and deep copy implements Serializable interface to read binary stream

In a real project, the prototype pattern rarely appears alone, typically in conjunction with the factory method pattern, creating an object through the clone method, and then providing it to the caller by the factory method.The prototype pattern is integrated with Java, and we can use it anywhere.

Next, we'll write a sample program code that combines a prototype pattern with a factory method pattern.

We have a HeroFactory factory that can produce different types of Mage (Magician), Warlord (Military), Beast (Veterinary King) from the Orcs and Elves, but these types of products are not directly New born in the factory, they are clone-based through existing types of objects in prototype mode.

First we draw the UML class diagram for this program as follows:


Next, according to the class diagram, step one: Write a Prototype abstract class

/**
 * 
 * Prototype
 *
 */
public abstract class Prototype implements Cloneable {

  public abstract Object copy() throws CloneNotSupportedException;

}

Step 2: Write an abstract class of Beast, Warlord, Mage that inherits Prototype

/**
 * 
 * Beast
 *
 */
public abstract class Beast extends Prototype {

  @Override
  public abstract Beast copy() throws CloneNotSupportedException;

}
/**
 * 
 * Mage
 *
 */
public abstract class Mage extends Prototype {

  @Override
  public abstract Mage copy() throws CloneNotSupportedException;

}

/**
 * 
 * Warlord
 *
 */
public abstract class Warlord extends Prototype {

  @Override
  public abstract Warlord copy() throws CloneNotSupportedException;

}

Step 3: Write specific subclasses of elves and orcs, inherit abstract classes defined in step 2

public class ElfBeast extends Beast {
  
  private String helpType;

  public ElfBeast(String helpType) {
    this.helpType = helpType;
  }

  public ElfBeast(ElfBeast elfBeast) {
    this.helpType = elfBeast.helpType;
  }

  @Override
  public Beast copy() throws CloneNotSupportedException {
    return new ElfBeast(this);
  }

  @Override
  public String toString() {
    return "Elven eagle helps in " + helpType;
  }

}

public class ElfMage extends Mage {

  
  private String helpType;
  
  public ElfMage(String helpType) {
    this.helpType = helpType;
  }

  public ElfMage(ElfMage elfMage) {
    this.helpType = elfMage.helpType;
  }

  @Override
  public ElfMage copy() throws CloneNotSupportedException {
    return new ElfMage(this);
  }

  @Override
  public String toString() {
    return "Elven mage helps in " + helpType;
  }

}

/**
 * 
 * ElfWarlord
 *
 */
public class ElfWarlord extends Warlord {

  private String helpType;
  
  public ElfWarlord(String helpType) {
    this.helpType = helpType;
  }

  public ElfWarlord(ElfWarlord elfWarlord) {
    this.helpType = elfWarlord.helpType;
  }

  @Override
  public ElfWarlord copy() throws CloneNotSupportedException {
    return new ElfWarlord(this);
  }

  @Override
  public String toString() {
    return "Elven warlord helps in " + helpType;
  }

}

public class OrcBeast extends Beast {
  
  private String weapon;

  public OrcBeast(String weapon) {
    this.weapon = weapon;
  }
  
  public OrcBeast(OrcBeast orcBeast) {
    this.weapon = orcBeast.weapon;
  }

  @Override
  public Beast copy() throws CloneNotSupportedException {
    return new OrcBeast(this);
  }

  @Override
  public String toString() {
    return "Orcish wolf attacks with " + weapon;
  }
  

}
//The remaining OrcMage, OrcWarlord can be written like Elf above...

Step 4: Write the HeroFactory interface and its implementation class:

/**
 * 
 * Interface for the factory class.
 * 
 */
public interface HeroFactory {

  Mage createMage();

  Warlord createWarlord();

  Beast createBeast();

}

/**
 * 
 * Concrete factory class.
 * 
 */
public class HeroFactoryImpl implements HeroFactory {

  private Mage mage;
  private Warlord warlord;
  private Beast beast;

  /**
   * Constructor
   */
  public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) {
    this.mage = mage;
    this.warlord = warlord;
    this.beast = beast;
  }

  /**
   * Create mage
   */
  public Mage createMage() {
    try {
      return mage.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

  /**
   * Create warlord
   */
  public Warlord createWarlord() {
    try {
      return warlord.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

  /**
   * Create beast
   */
  public Beast createBeast() {
    try {
      return beast.copy();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

}

Step 5: Write the App client class:

public class App {

  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

  /**
   * Program entry point
   * 
   * @param args command line args
   */
  public static void main(String[] args) {
    HeroFactory factory;
    Mage mage;
    Warlord warlord;
    Beast beast;

    factory = new HeroFactoryImpl(new ElfMage("cooking"), new ElfWarlord("cleaning"), new ElfBeast("protecting"));
    mage = factory.createMage();
    warlord = factory.createWarlord();
    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());
  }
}

Finally, run the App class with the following program output:

17:04:21.430 [main] INFO com.iluwatar.prototype.App - Elven mage helps in cooking
17:04:21.433 [main] INFO com.iluwatar.prototype.App - Elven warlord helps in cleaning
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Elven eagle helps in protecting
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish mage attacks with axe
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish warlord attacks with sword
17:04:21.434 [main] INFO com.iluwatar.prototype.App - Orcish wolf attacks with laser

There are three main entrance roles in the prototype model:
1. Prototype role: Define the method used to duplicate an existing instance to generate a new one, as in the example above, the Prototype class.
2. Specific prototype roles: Implement methods for replicating existing instances to generate new instances, such as ElfBeast, ElfMage, ElfWarlord, Orc...
3. User role: Maintain a registry and provide a way to find the correct instance prototype.Finally, provide a way to get a new instance, which is used to delegate the method of replicating the instance to generate the new instance.HeroFactoryImpl is in the example above.

Above, I've finished the prototype mode here, and by now we've learned all about creating models in this design mode.

You can try to combine factory mode, abstract factory mode, singleton mode, builder mode and prototype mode you learned today to design your application by combining 1-2 design modes at a time. You will certainly get unexpected results and experience by designing your own.

In the next article we will learn about Adapter Pattern in Structural Mode

The code is not easy. If you like it, please give me a compliment. Keep an eye on me and I will try to keep updating it.

Posted by stilgar on Wed, 15 May 2019 05:27:04 -0700