Skip to content

#584 Try to refactor prototype pattern #1815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 75 additions & 79 deletions prototype/README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
---
layout: pattern
title: Prototype
folder: prototype
permalink: /patterns/prototype/
categories: Creational
language: en
tags:
- Gang Of Four
- Instantiation
layout: pattern title: Prototype folder: prototype permalink: /patterns/prototype/ categories: Creational language: en
tags:

- Gang Of Four
- Instantiation

---

## Intent

Specify the kinds of objects to create using a prototypical instance, and create new objects by
copying this prototype.
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

## Explanation

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 instances.
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 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! Lets not get into the details but the key point here is
> that it is all about cloning.

In plain words
Expand All @@ -31,60 +27,60 @@ In plain words

Wikipedia says

> 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 new
> 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 new
> objects.

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

**Programmatic Example**

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.
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
public interface Prototype {
Object copy();
Object copy();
}
```

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) {
}
public Beast(Beast source) {
}

@Override
public abstract Beast copy();
@Override
public abstract Beast copy();
}

@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
public class OrcBeast extends Beast {

private final String weapon;
private final String weapon;

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

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

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

Expand All @@ -96,60 +92,61 @@ classes to produce different kinds of creatures from prototypes.

```java
public interface HeroFactory {

Mage createMage();
Warlord createWarlord();
Beast createBeast();

Mage createMage();

Warlord createWarlord();

Beast createBeast();
}

@RequiredArgsConstructor
public class HeroFactoryImpl implements HeroFactory {

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

public Mage createMage() {
return mage.copy();
}
public Mage createMage() {
return mage.copy();
}

public Warlord createWarlord() {
return warlord.copy();
}
public Warlord createWarlord() {
return warlord.copy();
}

public Beast createBeast() {
return beast.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.
Now, we are able to show the full prototype pattern in action producing new creatures by cloning existing instances.

```java
var factory = new HeroFactoryImpl(
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(
);
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());
);
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.
Expand All @@ -169,14 +166,13 @@ Orcish wolf attacks with laser

## Applicability

Use the Prototype pattern when a system should be independent of how its products are created,
composed, represented and
Use the Prototype pattern when a system should be independent of how its products are created, composed, represented and

* When the classes to instantiate are specified at run-time, for example, by dynamic loading.
* To avoid building a class hierarchy of factories that parallels the class hierarchy of products.
* When instances of a class can have one of only a few different combinations of state. It may be
more convenient to install a corresponding number of prototypes and clone them rather than
instantiating the class manually, each time with the appropriate state.
* When instances of a class can have one of only a few different combinations of state. It may be more convenient to
install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time
with the appropriate state.
* When object creation is expensive compared to cloning.

## Known uses
Expand Down
Binary file modified prototype/etc/prototype.urm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 24 additions & 37 deletions prototype/etc/prototype.urm.puml
Original file line number Diff line number Diff line change
@@ -1,105 +1,92 @@
@startuml
package com.iluwatar.prototype {
abstract class Prototype {
+ copy() : T {abstract}
}
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
abstract class Beast {
+ Beast()
+ Beast(source : Beast)
+ copy() : Beast {abstract}
+ equals(obj : Object) : boolean
}
class ElfBeast {
- helpType : String
+ ElfBeast(elfBeast : ElfBeast)
+ ElfBeast(helpType : String)
+ copy() : ElfBeast
+ equals(obj : Object) : boolean
+ toString() : String
}
class ElfMage {
- helpType : String
+ ElfMage(elfMage : ElfMage)
+ ElfMage(helpType : String)
+ copy() : ElfMage
+ equals(obj : Object) : boolean
+ toString() : String
}
class ElfWarlord {
- helpType : String
+ ElfWarlord(elfWarlord : ElfWarlord)
+ ElfWarlord(helpType : String)
+ copy() : ElfWarlord
+ equals(obj : Object) : boolean
+ toString() : String
}
interface HeroFactory {
+ createBeast() : Beast {abstract}
+ createMage() : Mage {abstract}
+ createWarlord() : Warlord {abstract}
+ create(type : HeroTypes) : T {abstract}
}
class HeroFactoryImpl {
- beast : Beast
- mage : Mage
- warlord : Warlord
+ HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast)
+ createBeast() : Beast
+ createMage() : Mage
+ createWarlord() : Warlord
+ create(type : HeroTypes) : T
}
abstract class Mage {
+ Mage()
+ Mage(source : Mage)
+ copy() : Mage {abstract}
+ equals(obj : Object) : boolean
}
class OrcBeast {
- weapon : String
+ OrcBeast(orcBeast : OrcBeast)
+ OrcBeast(weapon : String)
+ copy() : OrcBeast
+ equals(obj : Object) : boolean
+ toString() : String
}
class OrcMage {
- weapon : String
+ OrcMage(orcMage : OrcMage)
+ OrcMage(weapon : String)
+ copy() : OrcMage
+ equals(obj : Object) : boolean
+ toString() : String
}
class OrcWarlord {
- weapon : String
+ OrcWarlord(orcWarlord : OrcWarlord)
+ OrcWarlord(weapon : String)
+ copy() : OrcWarlord
+ equals(obj : Object) : boolean
+ toString() : String
}
interface Prototype {
+ copy() : Object {abstract}
}

abstract class Warlord {
+ Warlord()
+ Warlord(source : Warlord)
+ copy() : Warlord {abstract}
+ equals(obj : Object) : boolean
}
enum HeroTypes {
BEAST,
MAGE,
WARLORD
}
}

Beast ..|> Prototype
ElfBeast --|> Beast
OrcBeast --|> Beast
Mage ..|> Prototype
ElfMage --|> Mage
OrcMage --|> Mage
Warlord ..|> Prototype
ElfWarlord --|> Warlord
OrcWarlord --|> Warlord
HeroFactory ..+ App
HeroTypes ..+ HeroFactory
HeroFactoryImpl --> "-beast" Beast
HeroFactoryImpl --> "-warlord" Warlord
HeroFactoryImpl --> "-mage" Mage
Beast ..|> Prototype
ElfBeast --|> Beast
ElfMage --|> Mage
ElfWarlord --|> Warlord
HeroFactoryImpl ..|> HeroFactory
Mage ..|> Prototype
OrcBeast --|> Beast
OrcMage --|> Mage
OrcWarlord --|> Warlord
Warlord ..|> Prototype
HeroFactoryImpl ..|> HeroFactory
@enduml
Loading