原型模式(原型設(shè)計(jì)模式)詳解
在有些系統(tǒng)中朽们,存在大量相同或相似對(duì)象的創(chuàng)建問題,如果用傳統(tǒng)的構(gòu)造函數(shù)來創(chuàng)建對(duì)象诉位,會(huì)比較復(fù)雜且耗時(shí)耗資源骑脱,用原型模式生成對(duì)象就很高效,就像孫悟空拔下猴毛輕輕一吹就變出很多孫悟空一樣簡(jiǎn)單苍糠。
原型模式的定義與特點(diǎn)
原型(Prototype)模式的定義如下:用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型叁丧,通過復(fù)制該原型對(duì)象來創(chuàng)建一個(gè)和原型相同或相似的新對(duì)象。在這里岳瞭,原型實(shí)例指定了要?jiǎng)?chuàng)建的對(duì)象的種類拥娄。用這種方式創(chuàng)建對(duì)象非常高效,根本無須知道對(duì)象創(chuàng)建的細(xì)節(jié)瞳筏。
- 例如稚瘾,Windows 操作系統(tǒng)的安裝通常較耗時(shí),如果復(fù)制就快了很多姚炕。在生活中復(fù)制的例子非常多摊欠,這里不一一列舉了。
原型模式的結(jié)構(gòu)與實(shí)現(xiàn)
由于 Java 提供了對(duì)象的 clone() 方法柱宦,所以用 Java 實(shí)現(xiàn)原型模式很簡(jiǎn)單些椒。
1. 模式的結(jié)構(gòu)
原型模式包含以下主要角色。
- 抽象原型類:規(guī)定了具體原型對(duì)象必須實(shí)現(xiàn)的接口掸刊。
- 具體原型類:實(shí)現(xiàn)抽象原型類的 clone() 方法免糕,它是可被復(fù)制的對(duì)象。
- 訪問類:使用具體原型類中的 clone() 方法來復(fù)制新的對(duì)象。
其結(jié)構(gòu)圖如圖 1 所示说墨。
? 圖1 原型模式的結(jié)構(gòu)圖
2. 模式的實(shí)現(xiàn)
原型模式的克隆分為淺克隆和深克隆骏全,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實(shí)現(xiàn) Cloneable 接口就可實(shí)現(xiàn)對(duì)象的淺克隆尼斧,這里的 Cloneable 接口就是抽象原型類姜贡。其代碼如下:
//具體原型類
class Realizetype implements Cloneable
{
Realizetype()
{
System.out.println("具體原型創(chuàng)建成功!");
}
public Object clone() throws CloneNotSupportedException
{
System.out.println("具體原型復(fù)制成功棺棵!");
return (Realizetype)super.clone();
}
}
//原型模式的測(cè)試類
public class PrototypeTest
{
public static void main(String[] args)throws CloneNotSupportedException
{
Realizetype obj1=new Realizetype();
Realizetype obj2=(Realizetype)obj1.clone();
System.out.println("obj1==obj2?"+(obj1==obj2));
}
}
程序的運(yùn)行結(jié)果如下:
具體原型創(chuàng)建成功楼咳!
具體原型復(fù)制成功!
obj1==obj2?false
原型模式的應(yīng)用實(shí)例
用原型模式模擬“孫悟空”復(fù)制自己例子烛恤。
分析:孫悟空拔下猴毛輕輕一吹就變出很多孫悟空母怜,這實(shí)際上是用到了原型模式。這里的孫悟空類 SunWukong 是具體原型類缚柏,而 Java 中的 Cloneable 接口是抽象原型類苹熏。
同前面介紹的豬八戒實(shí)例一樣,由于要顯示孫悟空的圖像(點(diǎn)擊此處下載該程序所要顯示的孫悟空的圖片)币喧,所以將孫悟空類定義成面板 JPanel 的子類轨域,里面包含了標(biāo)簽,用于保存孫悟空的圖像杀餐。
另外干发,重寫了 Cloneable 接口的 clone() 方法,用于復(fù)制新的孫悟空史翘。訪問類可以通過調(diào)用孫悟空的 clone() 方法復(fù)制多個(gè)孫悟空枉长,并在框架窗體 JFrame 中顯示。圖 2 所示是其結(jié)構(gòu)圖琼讽。
? 圖2 孫悟空生成器的結(jié)構(gòu)圖
程序代碼如下:
import java.awt.*;
import javax.swing.*;
class SunWukong extends JPanel implements Cloneable
{
private static final long serialVersionUID = 5543049531872119328L;
public SunWukong()
{
JLabel l1=new JLabel(new ImageIcon("src/Wukong.jpg"));
this.add(l1);
}
public Object clone()
{
SunWukong w=null;
try
{
w=(SunWukong)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("拷貝悟空失敗!");
}
return w;
}
}
public class ProtoTypeWukong
{
public static void main(String[] args)
{
JFrame jf=new JFrame("原型模式測(cè)試");
jf.setLayout(new GridLayout(1,2));
Container contentPane=jf.getContentPane();
SunWukong obj1=new SunWukong();
contentPane.add(obj1);
SunWukong obj2=(SunWukong)obj1.clone();
contentPane.add(obj2);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
? 程序的運(yùn)行結(jié)果如圖 3 所示必峰。
? 圖3 孫悟空克隆器的運(yùn)行結(jié)果
用原型模式除了可以生成相同的對(duì)象,還可以生成相似的對(duì)象跨琳,請(qǐng)看以下實(shí)例自点。
原型模式生成“三好學(xué)生”獎(jiǎng)狀桐罕。
分析:同一學(xué)校的“三好學(xué)生”獎(jiǎng)狀除了獲獎(jiǎng)人姓名不同脉让,其他都相同,屬于相似對(duì)象的復(fù)制功炮,同樣可以用原型模式創(chuàng)建溅潜,然后再做簡(jiǎn)單修改就可以了。圖 4 所示是三好學(xué)生獎(jiǎng)狀生成器的結(jié)構(gòu)圖薪伏。
圖4 獎(jiǎng)狀生成器的結(jié)構(gòu)圖
程序代碼如下:
public class ProtoTypeCitation
{
public static void main(String[] args) throws CloneNotSupportedException
{
citation obj1=new citation("張三","同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀滚澜,被評(píng)為三好學(xué)生。","韶關(guān)學(xué)院");
obj1.display();
citation obj2=(citation) obj1.clone();
obj2.setName("李四");
obj2.display();
}
}
//獎(jiǎng)狀類
class citation implements Cloneable
{
String name;
String info;
String college;
citation(String name,String info,String college)
{
this.name=name;
this.info=info;
this.college=college;
System.out.println("獎(jiǎng)狀創(chuàng)建成功嫁怀!");
}
void setName(String name)
{
this.name=name;
}
String getName()
{
return(this.name);
}
void display()
{
System.out.println(name+info+college);
}
public Object clone() throws CloneNotSupportedException
{
System.out.println("獎(jiǎng)狀拷貝成功设捐!");
return (citation)super.clone();
}
}
程序運(yùn)行結(jié)果如下:
獎(jiǎng)狀創(chuàng)建成功借浊!
張三同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀,被評(píng)為三好學(xué)生萝招。韶關(guān)學(xué)院
獎(jiǎng)狀拷貝成功蚂斤!
李四同學(xué):在2016學(xué)年第一學(xué)期中表現(xiàn)優(yōu)秀,被評(píng)為三好學(xué)生槐沼。韶關(guān)學(xué)院
原型模式的應(yīng)用場(chǎng)景
原型模式通常適用于以下場(chǎng)景曙蒸。
- 對(duì)象之間相同或相似,即只是個(gè)別的幾個(gè)屬性不同的時(shí)候岗钩。
- 對(duì)象的創(chuàng)建過程比較麻煩纽窟,但復(fù)制比較簡(jiǎn)單的時(shí)候。
原型模式的擴(kuò)展
原型模式可擴(kuò)展為帶原型管理器的原型模式兼吓,它在原型模式的基礎(chǔ)上增加了一個(gè)原型管理器 PrototypeManager 類臂港。該類用 HashMap 保存多個(gè)復(fù)制的原型,Client 類可以通過管理器的 get(String id) 方法從中獲取復(fù)制的原型视搏。其結(jié)構(gòu)圖如圖 5 所示趋艘。
? 圖5 帶原型管理器的原型模式的結(jié)構(gòu)圖
原型管理器生成圖形例子
用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型,并計(jì)算其面積凶朗。分析:本實(shí)例中由于存在不同的圖形類瓷胧,例如,“圓”和“正方形”棚愤,它們計(jì)算面積的方法不一樣搓萧,所以需要用一個(gè)原型管理器來管理它們,圖 6 所示是其結(jié)構(gòu)圖宛畦。
? 圖6 圖形生成器的結(jié)構(gòu)圖
程序代碼如下:
import java.util.*;
interface Shape extends Cloneable
{
public Object clone(); //拷貝
public void countArea(); //計(jì)算面積
}
class Circle implements Shape
{
public Object clone()
{
Circle w=null;
try
{
w=(Circle)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("拷貝圓失敗!");
}
return w;
}
public void countArea()
{
int r=0;
System.out.print("這是一個(gè)圓瘸洛,請(qǐng)輸入圓的半徑:");
Scanner input=new Scanner(System.in);
r=input.nextInt();
System.out.println("該圓的面積="+3.1415*r*r+"\n");
}
}
class Square implements Shape
{
public Object clone()
{
Square b=null;
try
{
b=(Square)super.clone();
}
catch(CloneNotSupportedException e)
{
System.out.println("拷貝正方形失敗!");
}
return b;
}
public void countArea()
{
int a=0;
System.out.print("這是一個(gè)正方形,請(qǐng)輸入它的邊長(zhǎng):");
Scanner input=new Scanner(System.in);
a=input.nextInt();
System.out.println("該正方形的面積="+a*a+"\n");
}
}
class ProtoTypeManager
{
private HashMap<String, Shape>ht=new HashMap<String,Shape>();
public ProtoTypeManager()
{
ht.put("Circle",new Circle());
ht.put("Square",new Square());
}
public void addshape(String key,Shape obj)
{
ht.put(key,obj);
}
public Shape getShape(String key)
{
Shape temp=ht.get(key);
return (Shape) temp.clone();
}
}
public class ProtoTypeShape
{
public static void main(String[] args)
{
ProtoTypeManager pm=new ProtoTypeManager();
Shape obj1=(Circle)pm.getShape("Circle");
obj1.countArea();
Shape obj2=(Shape)pm.getShape("Square");
obj2.countArea();
}
}
運(yùn)行結(jié)果如下所示:
這是一個(gè)圓次和,請(qǐng)輸入圓的半徑:3
該圓的面積=28.2735
這是一個(gè)正方形反肋,請(qǐng)輸入它的邊長(zhǎng):3
該正方形的面積=9