fork download
  1. import java.util.*;
  2.  
  3. /*
  4. Today I went through Prototype.
  5.  
  6. A prototype is a base object. I already have one. I need more like it, so I copy it.
  7. The class should give me clone(). I should not build the copy in the client.
  8.  
  9. If I copy from outside it breaks:
  10. - private fields, I cannot read them
  11. - I often only have a Bird, I do not know Pigeon or Crow
  12. - if-else on every new bird breaks Open-Closed and SRP
  13. - client gets tied to every subclass
  14.  
  15. return this is not a copy. Same address means same object.
  16. A copy is a new object, different address, same state.
  17.  
  18. Inheritance is where I got stuck.
  19. If only Bird has clone(), Crow.clone() still runs Bird's method. I asked for a Crow, I get a Bird.
  20. I cannot copy parent private fields from Crow.
  21. super.clone() returns a Bird. I cannot downcast that into a Crow.
  22. Making fields protected just to clone is like making them public. I did not do that.
  23.  
  24. Fix I used: copy constructor on every class.
  25. Child calls super(old), then copies its own fields.
  26. Every child must override clone(), or the parent version runs.
  27.  
  28. Registry is a different problem.
  29. Intelligent pigeon is not a new class. It is a pigeon with weight 20 and brain 3.
  30. I store that object by name and always return a clone, never the original.
  31. */
  32.  
  33. interface Clonable<T> {
  34. T clone(); // T so Crow clone returns Crow, not Bird
  35. }
  36.  
  37. class Bird implements Clonable<Bird> {
  38. private String name;
  39. private String color;
  40. private int weight;
  41.  
  42. public Bird() {} // only for the first object I create
  43.  
  44. // takes the old bird and copies its fields into this new one
  45. public Bird(Bird old) {
  46. this.name = old.name;
  47. this.color = old.color;
  48. this.weight = old.weight;
  49. }
  50.  
  51. public Bird clone() {
  52. return new Bird(this); // not return this
  53. }
  54.  
  55. public void setName(String name) { this.name = name; }
  56. public void setColor(String color) { this.color = color; }
  57. public void setWeight(int weight) { this.weight = weight; }
  58.  
  59. public String toString() {
  60. return getClass().getSimpleName()
  61. + "{name=" + name + ", color=" + color + ", weight=" + weight + "}";
  62. }
  63. }
  64.  
  65. class Crow extends Bird {
  66. private String sound;
  67.  
  68. public Crow() {}
  69.  
  70. public Crow(Crow old) {
  71. super(old); // parent copies name, color, weight
  72. this.sound = old.sound; // I copy only what Crow added
  73. }
  74.  
  75. // if I skip this, Bird.clone() runs and the copy is a Bird
  76. public Crow clone() {
  77. return new Crow(this);
  78. }
  79.  
  80. public void setSound(String sound) { this.sound = sound; }
  81.  
  82. public String toString() {
  83. return super.toString() + ", sound=" + sound;
  84. }
  85. }
  86.  
  87. class Sparrow extends Bird {
  88. private int legSize;
  89.  
  90. public Sparrow() {}
  91.  
  92. public Sparrow(Sparrow old) {
  93. super(old);
  94. this.legSize = old.legSize;
  95. }
  96.  
  97. public Sparrow clone() {
  98. return new Sparrow(this);
  99. }
  100.  
  101. public void setLegSize(int legSize) { this.legSize = legSize; }
  102.  
  103. public String toString() {
  104. return super.toString() + ", legSize=" + legSize;
  105. }
  106. }
  107.  
  108. class Pigeon extends Bird {
  109. private int brain;
  110.  
  111. public Pigeon() {}
  112.  
  113. public Pigeon(Pigeon old) {
  114. super(old);
  115. this.brain = old.brain;
  116. }
  117.  
  118. public Pigeon clone() {
  119. return new Pigeon(this);
  120. }
  121.  
  122. public void setBrain(int brain) { this.brain = brain; }
  123.  
  124. public String toString() {
  125. return super.toString() + ", brain=" + brain;
  126. }
  127. }
  128.  
  129. // registry: named templates. get() must clone, or the caller changes my stored bird
  130. class BirdRegistry {
  131. private Map<String, Bird> birds = new HashMap<>();
  132.  
  133. public void register(String name, Bird bird) {
  134. birds.put(name, bird);
  135. }
  136.  
  137. public Bird get(String name) {
  138. Bird stored = birds.get(name);
  139. if (stored == null) return null;
  140. return stored.clone();
  141. }
  142. }
  143.  
  144. // notebook case: keep one base, copy it, then set only the cover and facts pages
  145. class Notebook implements Clonable<Notebook> {
  146. private String size;
  147. private int pages;
  148. private String rule;
  149. private int price;
  150. private String cover;
  151. private int factsPages;
  152.  
  153. public Notebook() {}
  154.  
  155. public Notebook(Notebook old) {
  156. this.size = old.size;
  157. this.pages = old.pages;
  158. this.rule = old.rule;
  159. this.price = old.price;
  160. this.cover = old.cover;
  161. this.factsPages = old.factsPages;
  162. }
  163.  
  164. public Notebook clone() {
  165. return new Notebook(this);
  166. }
  167.  
  168. public void setSize(String size) { this.size = size; }
  169. public void setPages(int pages) { this.pages = pages; }
  170. public void setRule(String rule) { this.rule = rule; }
  171. public void setPrice(int price) { this.price = price; }
  172. public void setCover(String cover) { this.cover = cover; }
  173. public void setFactsPages(int factsPages) { this.factsPages = factsPages; }
  174.  
  175. public String toString() {
  176. return "Notebook{size=" + size + ", pages=" + pages + ", rule=" + rule
  177. + ", price=" + price + ", cover=" + cover + ", factsPages=" + factsPages + "}";
  178. }
  179. }
  180.  
  181. public class Main {
  182. // sanctuary only has Bird, so no if-else here. each bird copies itself
  183. static List<Bird> reproduce(List<Bird> parents) {
  184. List<Bird> children = new ArrayList<>();
  185. for (Bird parent : parents) {
  186. children.add(parent.clone());
  187. }
  188. return children;
  189. }
  190.  
  191. public static void main(String[] args) {
  192. Crow crow = new Crow();
  193. crow.setName("sweet-sound");
  194. crow.setColor("black");
  195. crow.setWeight(34);
  196. crow.setSound("caw");
  197.  
  198. Sparrow sparrow = new Sparrow();
  199. sparrow.setName("long-legged");
  200. sparrow.setColor("blue");
  201. sparrow.setWeight(8);
  202. sparrow.setLegSize(8);
  203.  
  204. // not a subclass. just a pigeon with these values
  205. Pigeon intelligent = new Pigeon();
  206. intelligent.setName("intelligent-pigeon");
  207. intelligent.setColor("grey");
  208. intelligent.setWeight(20);
  209. intelligent.setBrain(3);
  210.  
  211. List<Bird> parents = Arrays.asList(crow, sparrow, intelligent);
  212. List<Bird> children = reproduce(parents);
  213.  
  214. // false = real copy
  215. System.out.println("same object? " + (parents.get(0) == children.get(0)));
  216. // should print Crow, not Bird
  217. System.out.println("child type: " + children.get(0).getClass().getSimpleName());
  218. System.out.println(children.get(0));
  219. System.out.println(children.get(1));
  220.  
  221. BirdRegistry registry = new BirdRegistry();
  222. registry.register("sweet-sound-crow", crow);
  223. registry.register("long-legged-sparrow", sparrow);
  224. registry.register("intelligent-pigeon", intelligent);
  225.  
  226. Bird copy1 = registry.get("sweet-sound-crow");
  227. Bird copy2 = registry.get("sweet-sound-crow");
  228. // two gets, two objects, one stored crow
  229. System.out.println("two crows from one prototype, same object? " + (copy1 == copy2));
  230. System.out.println(copy1);
  231.  
  232. Notebook base = new Notebook();
  233. base.setSize("A4");
  234. base.setPages(120);
  235. base.setRule("ruled");
  236. base.setPrice(40);
  237.  
  238. Notebook custom = base.clone();
  239. custom.setCover("red");
  240. custom.setFactsPages(4);
  241. System.out.println(custom);
  242. }
  243. }
Success #stdin #stdout 0.24s 59084KB
stdin
Standard input is empty
stdout
same object? false
child type: Crow
Crow{name=sweet-sound, color=black, weight=34}, sound=caw
Sparrow{name=long-legged, color=blue, weight=8}, legSize=8
two crows from one prototype, same object? false
Crow{name=sweet-sound, color=black, weight=34}, sound=caw
Notebook{size=A4, pages=120, rule=ruled, price=40, cover=red, factsPages=4}