创建型设计模式 之 原型模式

同为创建型模式的原型模式与单例模式是密不可分的,这也是最常用的设计模式之一。

原型模式是一种非常简单的设计模式。这里除了基本介绍和演示,还详细介绍了Java中原型模式的本质。

一、介绍

  同样,先来看一下《研磨设计模式》的定义——用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

  原型模式的本质——克隆生成对象。

  那么原型模式是什么意思呢?说白了就是克隆自身。我们知道Java中没有引用这个概念,Java用变量名代表引用。像 Apple a = new Apple();我们知道,想要操作这个Apple对象,那么就是去操作"a"这个变量名,实质就是去操作“a”这个引用所指向的内存地址上的Apple对象。通常而言,赋值操作符“=”的本质就是将“=”右边的地址赋值给左边的引用。如果我们希望创建一个跟这个对象a一样的Apple对象,同时在操作这个新对象的时候,对象a无任何影响。Java新手可能下意识觉得使用Apple b = a; 很明显a和b指向的是同一片内存空间。

  原型模式就是为了解决这样的对象复制的问题。

  Java中的原型模式实现起来其实很简单,在对象的接口中添加一个复制自身的抽象方法,然后对象实现这个方法,复制自身即可。使用的时候直接调用接口方法即可。

二、我的实现

1、我们有一个水果接口,如下:

1 public interface Fruit {
2
3     public double getPrice();
4
5     public void setPrice(double price);
6
7     //克隆接口
8     public Fruit cloneFruit();
9 }

2、一个简单实现类:

 1 public class Apple implements Fruit,Cloneable {
 2
 3     // 价格
 4     private double price;
 5     // 平均尺寸
 6     private double avgSize;
 7     // 产地
 8     private String productionArea;
 9
10     public Apple(double price, double avgSize, String productionArea) {
11         super();
12         this.price = price;
13         this.avgSize = avgSize;
14         this.productionArea = productionArea;
15     }
16
17     public double getAvgSize() {
18         return avgSize;
19     }
20
21     public void setAvgSize(double avgSize) {
22         this.avgSize = avgSize;
23     }
24
25     public String getProductionArea() {
26         return productionArea;
27     }
28
29     public void setProductionArea(String productionArea) {
30         this.productionArea = productionArea;
31     }
32
33     @Override
34     public void setPrice(double price) {
35         // TODO Auto-generated method stub
36         this.price = price;
37     }
38
39     @Override
40     public double getPrice() {
41         // TODO Auto-generated method stub
42         return price;
43     }
44
45     @Override
46     public String toString() {
47         return "Apple [avgSize=" + avgSize + ", price=" + price
48                 + ", productionArea=" + productionArea + "]";
49     }
50
51     //克隆自身的实现
52     @Override
53     public Fruit cloneFruit()
54     {
55         return new Apple(price, avgSize, productionArea);
56     }
57
58 }

3、然后就可以测试了,如下:

 1 package prototype.myPrototype;
 2
 3 public class Client {
 4
 5     public static void main(String[] args)
 6     {
 7         Fruit fruit = new Apple(1,2,"红富士");
 8         System.out.println(fruit);
 9
10         //根据原对象克隆
11         Fruit cloneFruit = fruit.cloneFruit();
12         System.out.println(cloneFruit);
13         System.out.println("两个水果是同一个吗?"+(fruit==cloneFruit));
14
15     }
16 }

4、结果如下:

Apple [avgSize=2.0, price=1.0, productionArea=红富士]
Apple [avgSize=2.0, price=1.0, productionArea=红富士]
两个水果是同一个吗?false

如上,简单的实现了原型模式。也实现了接口与实现分离的克隆。在完全不知道对象类型的情况下完成了复制。

三、Java的原型模式(Object的clone()方法)

(1)、介绍

  Object类有一个clone()方法,这是java为实现原型模式准备的。

  要实现Java的克隆方法,要满足三个条件:

  1、调用对象实现了cloneable接口

  2、调用对象重写了public Object clone();方法。

  3、重写clone()时,调用super.clone();

  我们来看一下clone方法的原型

  protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException

  我们可以看到,这个方法是protected访问控制,同时是一个本地方法。也就是说,任何Object的子类都可以调用这个方法,但是Object对象自身不能调用这个方法。

  这个方法执行的时候,是使用RTTI(run-time type identification)的机制,动态得找到目前正在调用clone方法的那个reference,根据它的大小申请内存空间,然后进行bitwise的复制,将该对象的内存空间完全复制到新的空间中去,从而达到shallowcopy的目的。(这句话摘自百度知道)

(2)、问题:

  1、为什么调用对象要实现cloneable接口?

  因为clone方法执行的时候,会判断当前对象是否实现了Cloneable这个标识接口,如果没有实现,就抛出CloneNotSupportedException异常。

  2、为什么调用对象要重写public Object clone();方法,为什么要调用super.clone()?

  如果调用对象不重写public Object clone()方法,那么clone()方法都不会显示出来。调用clone()方法,直接编译错误。可是clone()方法不是protected的吗?

  我们来看一下clone();方法的API:

By convention, the returned object should be obtained by calling super.clone. If a class and all of its superclasses (except Object) obey this convention, it will be the case that x.clone().getClass() == x.getClass(). --按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()

  因为只有重写clone()方法,我们才能通过使用super.clone(),才能真正调用Object类的本地方法clone();

  从这里我们可以看到,clone()方法是很特殊的。不重写,会编译错误,应该涉及到编译器的优化。

(3)、实现:  

我们来Java的原型模式简单改一下上面的示例:

1、将Fruit接口中的克隆功能去掉,这里不列出了。

2、Apple类实现Fruit接口,也实现Cloneable接口,并重写clone()方法,如下:

 1 public class Apple implements Fruit, Cloneable {
 2
 3     // 价格
 4     private double price;
 5     // 平均尺寸
 6     private double avgSize;
 7     // 产地
 8     private String productionArea;
 9
10     public Apple(double price, double avgSize, String productionArea)
11     {
12         super();
13         this.price = price;
14         this.avgSize = avgSize;
15         this.productionArea = productionArea;
16     }
17
18     public double getAvgSize()
19     {
20         return avgSize;
21     }
22
23     public void setAvgSize(double avgSize)
24     {
25         this.avgSize = avgSize;
26     }
27
28     public String getProductionArea()
29     {
30         return productionArea;
31     }
32
33     public void setProductionArea(String productionArea)
34     {
35         this.productionArea = productionArea;
36     }
37
38     @Override
39     public void setPrice(double price)
40     {
41         // TODO Auto-generated method stub
42         this.price = price;
43     }
44
45     @Override
46     public double getPrice()
47     {
48         // TODO Auto-generated method stub
49         return price;
50     }
51
52     // 重写了Object类的clone()方法
53     @Override
54     public Object clone()
55     {
56         // TODO Auto-generated method stub
57         Object cloneApple = null;
58         // 直接调用父类的克隆方法即可
59         try
60         {
61             cloneApple = super.clone();
62         } catch (CloneNotSupportedException e)
63         {
64             e.printStackTrace();
65         }
66         return cloneApple;
67     }
68
69     @Override
70     public String toString()
71     {
72         return "Apple [avgSize=" + avgSize + ", price=" + price + ", productionArea=" + productionArea + "]";
73     }
74
75 }

3、测试一下:

 1 public class Client {
 2
 3     static Fruit fruit = new Apple(90, 5, "新疆");
 4
 5     public static void main(String[] args) {
 6         System.out.println("Fruit:" + fruit);
 7         Fruit newFruit = (Fruit) ((Apple)fruit).clone();
 8         System.out.println("new Fruit : " + newFruit);
 9         System.out.println("前后是否为同一对象?" + (fruit == newFruit));
10     }
11 }

4、结果:

Fruit:Apple [avgSize=5.0, price=90.0, productionArea=新疆]
new Fruit : Apple [avgSize=5.0, price=90.0, productionArea=新疆]
前后是否为同一对象?false

个人认为,方便而言,还不如自己手动实现原型模式。

由于Java的原型使用RTTI的机制,速度块,若需要克隆重量级或复杂的对象时适合使用。不过要注意的是,Java的原型模式属于浅度克隆。下面我们会讲如何使用Java的原型模式实现深度克隆。

这里再补充一点:

我们来看,static Fruit fruit = new Apple(90, 5, "新疆");

对于对象fruit,它的编译时类型是Fruit,运行时类型是Apple,所以fruit对象只能显式调用Fruit接口所有的方法。而这其中包含了Object类除了clone()方法之外的所有方法。Java中,接口和类是一个并列的概念

我们可以认为接口是一种特殊的类,它默认实现了Object中除了clone()之外的像toString()、getClass等一系列Object方法。

四、深度克隆和浅度克隆

  克隆分为深度克隆和浅度克隆。假设我们现在有一个对象,这个对象有一个引用属性是另一个对象。我们知道,引用属性的本质就是一个内存地址。按照上文的复制,我们会将复制对象的引用属性也一同复制,这样一来,复制对象和被复制对象的该属性都指向同一内存地址了。这就违背了我们克隆的初衷。而这个就是浅度克隆。

  相对的,深度克隆就是克隆之后,复制对象和被复制对象是完全不想干的。即对于对象的引用属性,我们重新克隆一次。

  下面是Java克隆、深度克隆的简单示例:

1、首先是一个包含刚才Fruit类的Businessman对象:

@Data
public class Businessman implements Cloneable {

private String name;
private Fruit fruit;

public Businessman(String name, Fruit fruit)
{
super();
this.name = name;
this.fruit = fruit;
}

//克隆方法
public Object clone(){
//先克隆一下引用变量
Fruit f = (Fruit) ((Apple)fruit).clone();
Businessman man = new Businessman(name,f);
return man;
}
}

2、测试一下:

 1 package prototype;
 2
 3 public class Client {
 4
 5     public static void main(String[] args) {
 6         //水果类
 7         Fruit fruit = new Apple(90, 5, "新疆");
 8         //商人
 9         Businessman man1 = new Businessman("张三",fruit);
10
11         System.out.println("man1:" + man1);
12         //克隆
13         Businessman man2 = (Businessman) man1.clone();
14
15         System.out.println("man2 : " + man2);
16         System.out.println("man1和man2前后是否为同一对象?" + (man1 == man2));
17         System.out.println("两者的属性呢?"+(man1.getFruit()==man2.getFruit()));
18     }
19 }

3、结果如下:

man1:Businessman [name=张三, fruit=Apple [avgSize=5.0, price=90.0, productionArea=新疆]]
man2 : Businessman [name=张三, fruit=Apple [avgSize=5.0, price=90.0, productionArea=新疆]]
man1和man2前后是否为同一对象?false
两者的属性呢?false

  

原文地址:https://www.cnblogs.com/JiangWJ/p/10801449.html

时间: 2024-11-03 03:34:43

创建型设计模式 之 原型模式的相关文章

创建型设计模式之原型模式(Prototype)

结构   意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或者 当一个类的实例只能有几个不同状态组合中的一种时.建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些. 1 using System; 2 3 // Objects which are to work as prototypes must be based on class

java语言实现创建型设计模式—工厂方法模式

一.描述 基于简单工厂模式中将所有类的创建和初始化放在一个工厂类中出现的问题,我们引进了工厂方法模式,该模式是GoF总结的23种设计模式的第一种,这个设计模式将一个工厂类拆分成多个具体的工厂类,每个具体的工厂类负责相应的类的对象的创建. 在工厂方法模式中,抽象工厂类负责定义创建对象的接口,具体对象的创建由实现该抽象工厂的具体工厂类来完成,它由四部分组成:抽象工厂类.实现抽象工厂类的具体工厂类.抽象类.实现抽象类的具体类. 二.工厂方法模式的优缺点 优点:在工厂方法模式中,创建对象的任务由具体的工

创建型设计模式 之 建造者模式

一.介绍 生成器模式是什么呢? <研磨设计模式中>给了这样的定义:将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表示. 生成器模式的本质——分离整体构建算法和部件构造. 其实,生成器模式的目的,便是为了构建复杂的产品,将构建算法和构造实现分离出来,以便系统可以更好的优化.扩展. 理解生成器模式主要是理解生成器2个部件,一个是生成器(Builder),一个是指导者(Director).生成器指的是整个复杂对象的构建过程.构建算法.而指导者指的是对生成器所生成的部件对象的构造

创建型设计模式(工厂模式)

在工厂模式中,我们创建对象而不将创建逻辑暴露给客户端. 首先,我们设计一个接口来表示Shape. public interface Shape { void draw(); } 然后我们创建实现接口的具体类. public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public

创建型设计模式之工厂模式

结构            意图         提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 适用性     一个系统要独立于它的产品的创建.组合和表示时. 一个系统要由多个产品系列中的一个来配置时. 当你要强调一系列相关的产品对象的设计以便进行联合使用时. 当你提供一个产品类库,而只想显示它们的接口而不是实现时. 1 using System; 2 3 // These classes could be part of a framework, 4 // which

java语言实现创建型设计模式—抽象工厂模式

一.描述 抽象工厂模式是在工厂方法的模式上进一步抽象而来,如果说工厂方法模式是对一个产品结构的创建而言的话,那么抽象工厂模式则是针对多个产品结构而言的,它被用来一次创建多个不同的产品对象. 我们要创建一个摩托车轮胎和摩托车把手,又要创建一个自行车轮胎和自行车把手,如果我们使用工厂方法模式的话我们需要四个类:创建摩托车轮胎的工厂类,创建摩托车把手的工厂类,创建自行车轮胎的工厂类和创建自行车把手的工厂类,但是如果我们使用抽象工厂方法的话,我们需要创建两个工厂类:创建摩托车轮胎和把手的工厂类,创建自行

创建型设计模式总结(二)

上篇文章我们介绍了创建型设计模式的前三种:简单工厂模式.工厂方法模式和抽象工厂模式.这一篇文章,我们系统介绍一下其他的三种创建型设计模式:创建者模式.原型模式和单例模式. 一.创建者模式 创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象. 创建者模式通常包括如下几种角色: 1.建造者角色(Builder) 对复杂对象的

&quot;围观&quot;设计模式(29)--创建型设计模式总结(单例、工厂、原型、创建者)

设计模式源码下载地址 设计模式源码下载地址 1  单例模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为.比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简化了在复杂环境下的配置管理.----维基百科(WIKIPEDIA) 个人的理解: 单例模式概

[Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

注:关乎对象的创建方式的设计模式就是"创建型设计模式"(creational design pattern) 1.1 抽象工厂模式 "抽象工厂模式"(Abstract Factory Pattern)用来创建复杂的对象,这种对象由许多小对象组成,而这些小对象都属于某个特定的"系列"(family). 比如说,在GUI 系统里可以设计"抽象控件工厂"(abstract widget factory),并设计三个"具体子