原型模式
# 原型模式
# 一、原型模式
原理:原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。
优缺点
- 1 优点
- 性能提升:通过克隆现有对象可以避免重复创建相似对象的昂贵代价,尤其是创建对象的过程非常复杂或耗时的时候。
- 简化对象创建:避免了使用构造函数直接创建对象的复杂性,特别是当类有很多配置选项时,使用原型模式可以简化对象的创建过程。
- 动态配置对象:可以在运行时动态改变对象的配置,并且通过克隆这些配置好的对象来创建新实例,从而实现灵活的对象创建。
- 减少子类数量:通过克隆对象而不是通过继承来扩展对象的功能,可以减少子类的数量,降低系统的复杂性。
- 可以与其他模式结合使用:原型模式可以与其他设计模式(如工厂模式、单例模式)结合使用,提供更强大的功能。
- 2 缺点
- 克隆复杂对象时会有问题:如果对象包含复杂的引用类型(如嵌套对象、循环引用等),实现深度克隆会变得复杂和困难。
- 实现成本高:有些对象的复制操作可能并不容易实现,特别是当对象需要深度克隆时,需要手动编写克隆逻辑,这增加了开发的复杂度和成本。
- 潜在的安全问题:克隆对象时需要注意敏感数据的处理,避免不必要的数据泄露或安全隐患。
- 内存开销:在某些情况下,频繁地克隆对象可能会增加内存的使用,尤其是当克隆的对象数量很大时,这可能会成为性能瓶颈。
适用场景
- 对象创建成本高:当创建一个对象的代价非常高昂时,使用原型模式可以通过复制现有对象来节省开销。
- 需要动态创建对象:在运行时需要根据某些状态或配置动态创建对象,并且这些对象有相似的结构和配置。
- 避免使用子类创建对象:当系统中存在大量相似的对象,但又不希望通过增加子类来创建这些对象时,原型模式是一种合适的选择。
# 二、原型模式基本代码
浅复制
简历原型实现
public class Resume implements Cloneable{ private String name; private String sex; private String age; private String timeArea; private String company; public Resume(String name) { this.name = name; } public void setPersonalInfo(String sex, String age) { this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } public void display(){ System.out.println(this.name+" "+this.sex+" "+this.age); System.out.println("工作经历"+this.timeArea+" "+this.company); } // 省略get、set public Resume clone(){ Resume object = null; try { object = (Resume)super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Clone has error"); e.printStackTrace(); } return object; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37客户端代码
Resume resume1 = new Resume("小红"); resume1.setPersonalInfo("女","18"); resume1.setWorkExperience("2000-2003","a") Resume resume2 = resume1.clone(); resume2.setPersonalInfo("男","20"); resume2.setWorkExperience("2000-2005","b") resume1.display(); resume2.display();
1
2
3
4
5
6
7
8
9
10
深复制
工作经历类
public class WorkExperience implements Cloneable{ private String timeArea; private String company; // 省略get、set public WorkExperience clone(){ c object = null; try { object = (WorkExperience)super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Clone has error"); e.printStackTrace(); } return object; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17简历类
public class Resume implements Cloneable{ private String name; private String sex; private String age; private WorkExperience work; public Resume(String name) { this.name = name; this.work = new WorkExperience(); } public void setPersonalInfo(String sex, String age) { this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.work.setTimeArea(timeArea); this.work.setCompany(company); } public void display(){ System.out.println(this.name+" "+this.sex+" "+this.age); System.out.println("工作经历"+this.work.getTimeArea()+" "+this.work.getCompany()); } // 省略get、set public Resume clone(){ Resume object = null; try { object = (Resume)super.clone(); object.work = this,work.clone(); //需要将引用的对象也复制 } catch (CloneNotSupportedException e) { System.out.println("Clone has error"); e.printStackTrace(); } return object; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 三、实现方式
创建原型接口, 并在其中声明
克隆
方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。原型类必须另行定义一个以该类对象为参数的构造函数。 构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果你需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。
如果编程语言不支持方法重载, 那么你可能需要定义一个特殊方法来复制对象数据。 在构造函数中进行此类处理比较方便, 因为它在调用
new
运算符后会马上返回结果对象。克隆方法通常只有一行代码: 使用
new
运算符调用原型版本的构造函数。 注意, 每个类都必须显式重写克隆方法并使用自身类名调用new
运算符。 否则, 克隆方法可能会生成父类的对象。你还可以创建一个中心化原型注册表, 用于存储常用原型。
你可以新建一个工厂类来实现注册表, 或者在原型基类中添加一个获取原型的静态方法。 该方法必须能够根据客户端代码设定的条件进行搜索。 搜索条件可以是简单的字符串, 或者是一组复杂的搜索参数。 找到合适的原型后, 注册表应对原型进行克隆, 并将复制生成的对象返回给客户端。
最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。
编辑 (opens new window)