---
tags: Creational Patterns
---
# 原型模式 Prototype
## 大綱
有時候建立新的物件很高,如:讀取一次資料庫。
## 角色
**Prototype**: like the Graphic
宣告自我複製的介面。
**ConcretePrototype**: 具體實作出自我複製的方法。
like the Staff, WholeNote, HalfNote
**Client**:叫原型個體自我複製一份,以生出新的物件。

Client呼叫Prototype,或是進行註冊/刪除元件的動作
## 好處
* 將ConcreteProduct類別隱藏起來,減少Client所能知道的類別種類,也能不修改Client即可與其他應用領域的類別協同運作。
* 在執行期增刪成品物件類型:只要向Client註冊Prototype,就能引進新的成品物件類型,且Client可以在執行期安裝或卸除Prototype。
* 改變內容以訂出新的物件:可透過物件複合方式制定新的行為(如:指定物件的個體變數內容)。
* 改變結構以訂出新的物件:將小電路當原型個體,拿到電路元件上擺著。只要複合電路物件的clone()方法採取deep copy,就能將各種不同的電路結構視為原型個體來用。
* 減少子類別數量:不像 Factory Method Pattern 會產生與成品物件類別階層平行對應的Creator類別階層,只要複製原型個體即可。
## 壞處
* 每一個Prototype子類別都必須實作clone()方法,若是想把clone()加到早已存在的類別裡,或是類別裡有不支援複製動作的物件,或是有循環指涉(circular reference)的情形,就更不容易製作clone()。
## 雛型範例
```java=
//必須實作Cloneable且必須擲出錯誤訊息
public interface Prototype extends Cloneable {
public void getName();
public Prototype clone() throws CloneNotSupportedException;
}
//所有try-catch皆為必須
public class ConcretePrototype implements Prototype{
public Prototype clone(){
try {
return (Prototype) super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
```
## 形狀範例
```java=
//Cloneable是java的預設介面
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
//client
public class ShapeCache {
//紀錄註冊class list
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// for each shape run database query and create shape
// shapeMap.put(shapeKey, shape);
// for example, we are adding three shapes
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeMap.put(rectangle.getId(), rectangle);
}
}
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
```
## 字形外觀範例
```java=
//Client
public class Manager {
private Hashtable showcase = new Hashtable();
public void register(String name, Product proto) {
showcase.put(name, proto);
}
public Product create(String protoname) {
Product p = (Product)showcase.get(protoname);
return p.createClone();
}
}
//Prototype
public interface Product extends Cloneable {
public abstract void use(String s);
public abstract Product createClone();
}
//ConcreteProduct
public class UnderlinePen implements Product {
private char ulchar;
public UnderlinePen(char ulchar) {
this.ulchar = ulchar;
}
public void use(String s) {
int length = s.getBytes().length;
System.out.println("\"" + s + "\"");
System.out.print(" ");
for (int i = 0; i < length; i++) {
System.out.print(ulchar);
}
System.out.println("");
}
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
public class MessageBox implements Product {
private char decochar;
public MessageBox(char decochar) {
this.decochar = decochar;
}
public void use(String s) {
int length = s.getBytes().length;
for (int i = 0; i < length + 4; i++) {
System.out.print(decochar);
}
System.out.println("");
System.out.println(decochar + " " + s + " " + decochar);
for (int i = 0; i < length + 4; i++) {
System.out.print(decochar);
}
System.out.println("");
}
public Product createClone() {
Product p = null;
try {
p = (Product)clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
public class Main {
public static void main(String[] args) {
// 預備階段
Manager manager = new Manager();
UnderlinePen upen = new UnderlinePen('~');
MessageBox mbox = new MessageBox('*');
MessageBox sbox = new MessageBox('/');
manager.register("strong message", upen);
manager.register("warning box", mbox);
manager.register("slash box", sbox);
// 實作產生
Product p1 = manager.create("strong message");
p1.use("Hello, world.");
Product p2 = manager.create("warning box");
p2.use("Hello, world.");
Product p3 = manager.create("slash box");
p3.use("Hello, world.");
}
}
```
## shallow copy 淺層複製
## deep copy 深層複製
>[name=閔致]