import java.util.*;
/*
Today I went through Prototype.
A prototype is a base object. I already have one. I need more like it, so I copy it.
The class should give me clone(). I should not build the copy in the client.
If I copy from outside it breaks:
- private fields, I cannot read them
- I often only have a Bird, I do not know Pigeon or Crow
- if-else on every new bird breaks Open-Closed and SRP
- client gets tied to every subclass
return this is not a copy. Same address means same object.
A copy is a new object, different address, same state.
Inheritance is where I got stuck.
If only Bird has clone(), Crow.clone() still runs Bird's method. I asked for a Crow, I get a Bird.
I cannot copy parent private fields from Crow.
super.clone() returns a Bird. I cannot downcast that into a Crow.
Making fields protected just to clone is like making them public. I did not do that.
Fix I used: copy constructor on every class.
Child calls super(old), then copies its own fields.
Every child must override clone(), or the parent version runs.
Registry is a different problem.
Intelligent pigeon is not a new class. It is a pigeon with weight 20 and brain 3.
I store that object by name and always return a clone, never the original.
*/
interface Clonable<T> {
T clone(); // T so Crow clone returns Crow, not Bird
}
class Bird implements Clonable<Bird> {
private int weight;
public Bird() {} // only for the first object I create
// takes the old bird and copies its fields into this new one
public Bird(Bird old) {
this.name = old.name;
this.color = old.color;
this.weight = old.weight;
}
public Bird clone() {
return new Bird(this); // not return this
}
public void setName
(String name
) { this.
name = name
; } public void setColor
(String color
) { this.
color = color
; } public void setWeight(int weight) { this.weight = weight; }
return getClass().getSimpleName()
+ "{name=" + name + ", color=" + color + ", weight=" + weight + "}";
}
}
class Crow extends Bird {
public Crow() {}
public Crow(Crow old) {
super(old); // parent copies name, color, weight
this.sound = old.sound; // I copy only what Crow added
}
// if I skip this, Bird.clone() runs and the copy is a Bird
public Crow clone() {
return new Crow(this);
}
public void setSound
(String sound
) { this.
sound = sound
; }
return super.toString() + ", sound=" + sound;
}
}
class Sparrow extends Bird {
private int legSize;
public Sparrow() {}
public Sparrow(Sparrow old) {
super(old);
this.legSize = old.legSize;
}
public Sparrow clone() {
return new Sparrow(this);
}
public void setLegSize(int legSize) { this.legSize = legSize; }
return super.toString() + ", legSize=" + legSize;
}
}
class Pigeon extends Bird {
private int brain;
public Pigeon() {}
public Pigeon(Pigeon old) {
super(old);
this.brain = old.brain;
}
public Pigeon clone() {
return new Pigeon(this);
}
public void setBrain(int brain) { this.brain = brain; }
return super.toString() + ", brain=" + brain;
}
}
// registry: named templates. get() must clone, or the caller changes my stored bird
class BirdRegistry {
private Map
<String, Bird
> birds
= new HashMap
<>();
public void register
(String name, Bird bird
) { birds.put(name, bird);
}
public Bird get
(String name
) { Bird stored = birds.get(name);
if (stored == null) return null;
return stored.clone();
}
}
// notebook case: keep one base, copy it, then set only the cover and facts pages
class Notebook implements Clonable<Notebook> {
private int pages;
private int price;
private int factsPages;
public Notebook() {}
public Notebook(Notebook old) {
this.size = old.size;
this.pages = old.pages;
this.rule = old.rule;
this.price = old.price;
this.cover = old.cover;
this.factsPages = old.factsPages;
}
public Notebook clone() {
return new Notebook(this);
}
public void setSize
(String size
) { this.
size = size
; } public void setPages(int pages) { this.pages = pages; }
public void setRule
(String rule
) { this.
rule = rule
; } public void setPrice(int price) { this.price = price; }
public void setCover
(String cover
) { this.
cover = cover
; } public void setFactsPages(int factsPages) { this.factsPages = factsPages; }
return "Notebook{size=" + size + ", pages=" + pages + ", rule=" + rule
+ ", price=" + price + ", cover=" + cover + ", factsPages=" + factsPages + "}";
}
}
public class Main {
// sanctuary only has Bird, so no if-else here. each bird copies itself
static List<Bird> reproduce(List<Bird> parents) {
List<Bird> children = new ArrayList<>();
for (Bird parent : parents) {
children.add(parent.clone());
}
return children;
}
public static void main
(String[] args
) { Crow crow = new Crow();
crow.setName("sweet-sound");
crow.setColor("black");
crow.setWeight(34);
crow.setSound("caw");
Sparrow sparrow = new Sparrow();
sparrow.setName("long-legged");
sparrow.setColor("blue");
sparrow.setWeight(8);
sparrow.setLegSize(8);
// not a subclass. just a pigeon with these values
Pigeon intelligent = new Pigeon();
intelligent.setName("intelligent-pigeon");
intelligent.setColor("grey");
intelligent.setWeight(20);
intelligent.setBrain(3);
List
<Bird
> parents
= Arrays.
asList(crow, sparrow, intelligent
); List<Bird> children = reproduce(parents);
// false = real copy
System.
out.
println("same object? " + (parents.
get(0) == children.
get(0))); // should print Crow, not Bird
System.
out.
println("child type: " + children.
get(0).
getClass().
getSimpleName()); System.
out.
println(children.
get(0)); System.
out.
println(children.
get(1));
BirdRegistry registry = new BirdRegistry();
registry.register("sweet-sound-crow", crow);
registry.register("long-legged-sparrow", sparrow);
registry.register("intelligent-pigeon", intelligent);
Bird copy1 = registry.get("sweet-sound-crow");
Bird copy2 = registry.get("sweet-sound-crow");
// two gets, two objects, one stored crow
System.
out.
println("two crows from one prototype, same object? " + (copy1
== copy2
));
Notebook base = new Notebook();
base.setSize("A4");
base.setPages(120);
base.setRule("ruled");
base.setPrice(40);
Notebook custom = base.clone();
custom.setCover("red");
custom.setFactsPages(4);
}
}