原型模式
可以复制已有对象,从而创建一个和原型对象相同的新对象。使用这种方式创建对象无需知道对象创建的细节,所以创建效率高效。
实现原型结构条件:
- 实现Cloneable接口;
- 重写clone方法;
1.模式结构图
2.模式的结构
-
抽象原型类:规定了具体原型对象必须实现的接口。
-
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
-
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
3.模式的实现
3.1实现一
原型模式的克隆分为浅克隆和深克隆,Java中Object类提供的clone()方法属于浅克隆,Cloneable接口就是抽象原型;
具体原型类:
/**
* 具体原型类:视频
* 如何实现克隆:
* 1.实现cloneable接口
* 2.重写clone方法
*/
public class Video implements Cloneable{
private String name;
private Date date;
public Video(String name, Date date) {
this.name = name;
this.date = date;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 实现深克隆
Video video = (Video) super.clone();
//将对象属性也进行克隆
video.date = (Date) this.getDate().clone();
return video;
}
//getter/setter此处省略...
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", date=" + date +
'}';
}
}
访问类:
/**
* 访问类:客户端
* 原型模式:
* spring 中的bean 的创建有两种 :一种是单例模式 ,二是原型模式
*/
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
clone1();
System.out.println("\n");
clone2();
}
/**
* 克隆对象
*
* @throws CloneNotSupportedException
*/
public static void clone1() throws CloneNotSupportedException {
//原型对象 video1
Video video = new Video("UP主A-视频一", DateUtil.date());
System.out.println("video1 -> " + video);
System.out.println("video1 hash-> " + video.hashCode());
// UP主B 克隆 UP主A的视频
Video video2 = (Video) video.clone();
video2.setName("UP主B-视频一");
System.out.println("video2 -> " + video2);
System.out.println("video2 hash-> " + video2.hashCode());
System.out.println("UP主A-视频一 == UP主B-视频一:" + (video == video2));
}
/**
* 验证是否是浅克隆
*
* @throws CloneNotSupportedException
*/
public static void clone2() throws CloneNotSupportedException {
//克隆对象video3
Date date = new Date();
Video video = new Video("UP主A-视频一", date);
Video video2 = (Video) video.clone();
System.out.println("video1->" + video);
System.out.println("video1 hash-> " + video.hashCode());
System.out.println("video2->" + video2);
System.out.println("video2 hash-> " + video2.hashCode());
System.out.println("=================分割线==================");
/*
修改了date的值,video和video2 中的date都发生了变化
说明video1和video2同时指向了date,video2只是把video的值和引用copy过来了,是浅克隆
*/
date.setTime(1596596952);
System.out.println("video1->" + video);
System.out.println("video1 hash-> " + video.hashCode());
System.out.println("video2->" + video2);
System.out.println("video2 hash-> " + video2.hashCode());
}
}
运行结果:
3.2实现二
原型模式可扩展为带原型管理器的原型模式,它在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。
例:用带原型管理器的原型模式来生成包含“圆”和“三角形”等图形的原型,并计算其面积。分析:本实例中由于存在不同的图形类,例如,“圆”和“三角形”,它们计算面积的方法不一样,所以需要用一个原型管理器来管理它们,也可以通过原型管理器来添加新的图形原型;
- 原型结构图
- 代码展示
抽象原型类
/**
* 图形类
*/
public interface Shape extends Cloneable {
Object clone(); //拷贝
void countArea(); //计算面积
}
具体原型类
/**
* 圆形类
*/
public class Circle implements Shape {
//半径
private int r;
@Override
public Object clone() {
Circle circle = null;
try {
circle = (Circle) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝圆失败!");
}
return circle;
}
@Override
public void countArea() {
int r=0;
System.out.print("这是一个圆,请输入圆的半径:");
Scanner input=new Scanner(System.in);
r=input.nextInt();
System.out.println("该圆的面积="+Math.PI*r*r+"\n");
}
}
/* *************分割线************* */
/**
* 三角形
*/
public class Rectangle implements Shape {
//宽
public int width;
//高
public int height;
@Override
public Object clone() {
Rectangle rectangle = null;
try {
rectangle = (Rectangle) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝正方形失败!");
}
return rectangle;
}
@Override
public void countArea() {
System.out.print("这是一个三角形,请输入它的底:");
Scanner input = new Scanner(System.in);
width = input.nextInt();
System.out.print("这是一个三角形,请输入它的高:");
height = input.nextInt();
System.out.println("该三角形的面积=" + (width * height)/2 + "\n");
}
}
/* *************分割线************* */
/**
* 正方形
*/
public class Square implements Shape {
//边长
public int length;
public Object clone() {
Square square = null;
try {
square = (Square) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝正方形失败!");
}
return square;
}
public void countArea() {
System.out.print("这是一个正方形,请输入它的边长:");
Scanner input = new Scanner(System.in);
length = input.nextInt();
System.out.println("该正方形的面积=" + length * length + "\n");
}
}
原型管理器类
/**
* 原型管理器
*/
public class ProtoTypeManager {
private Map<String, Shape> map = new HashMap<String, Shape>();
public ProtoTypeManager() {
map.put("Circle", new Circle());
map.put("Rectangle", new Rectangle());
}
public void addShape(String key, Shape obj) {
map.put(key, obj);
}
public Shape getShape(String key) {
Shape temp = map.get(key);
return (Shape) temp.clone();
}
}
访问类
public class ProtoTypeShape {
public static void main(String[] args) {
ProtoTypeManager pm = new ProtoTypeManager();
Shape circle = pm.getShape("Circle");
circle.countArea();
Shape rectangle = pm.getShape("Rectangle");
rectangle.countArea();
pm.addShape("Square",new Square());
Shape square = pm.getShape("Square");
square.countArea();
}
}
/*================= 输出结果================
这是一个圆,请输入圆的半径:3
该圆的面积=28.274333882308138
这是一个三角形,请输入它的底:3
这是一个三角形,请输入它的高:2
该三角形的面积=3
这是一个正方形,请输入它的边长:3
该正方形的面积=9
================= 输出结果================*/