原型模式(创建型模式)
原创大约 4 分钟
模式概述
原型模式是一种创建型设计模式,它可以复制已有对象,而又无需依赖这些对象所属的类。意思就是可以它照着模子自己克隆出另一个产品,这也是它名字的由来。
问题
如果希望生成一个与现有对象完全相同的复制品,该如何实现呢?
首先,必须创建一个属于相同类的对象。
然后,必须遍历原对象的所有属性,并将其值复制到新对象中。
但如果这个要复制的对象的私有成员变量无法访问,该怎么办呢?另外,如果不知道这个复制对象属于什么类,那又该怎么办呢?
方案
原型模式为所有能够被克隆的对象声明了一个通用接口,以便能够克隆对象,而且又无需知道对象属于什么类,以及它有多少私有属性。
这个可以被克隆的对象就叫做 原型。
它的原作方式是:创建一系列不同类型的对象并以不同的方式配置它们。如果所需对象与这个配置对象中的某一个相同,那就可以直接克隆那个配置对象了,而无需以新建的方式得到对象。
结构

实现
Java
/**
* 抽象形状类
*
*/
public abstract class Shape {
public int x;
public int y;
public String color;
public Shape() {
}
public Shape(Shape target) {
if (target != null) {
this.x = target.x;
this.y = target.y;
this.color = target.color;
}
}
public abstract Shape clone();
@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Shape)) return false;
Shape shape2 = (Shape) object2;
return shape2.x == x && shape2.y == y && Objects.equals(shape2.color, color);
}
}
/**
* 圆形类
*
*/
public class Circle extends Shape {
public int radius;
public Circle() {
}
public Circle(Circle target) {
super(target);
if (target != null) {
this.radius = target.radius;
}
}
@Override
public Shape clone() {
return new Circle(this);
}
@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Circle) || !super.equals(object2)) return false;
Circle shape2 = (Circle) object2;
return shape2.radius == radius;
}
}
/**
* 客户端
*
*/
public class Client {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
List<Shape> copies = new ArrayList<>();
Circle circle = new Circle();
circle.x = 10;
circle.y = 20;
circle.radius = 15;
circle.color = "red";
shapes.add(circle);
Circle anotherCircle = (Circle) circle.clone();
shapes.add(anotherCircle);
cloneAndCompare(shapes, copies);
}
private static void cloneAndCompare(List<Shape> shapes, List<Shape> copies) {
for (Shape shape : shapes) {
copies.add(shape.clone());
}
for (int i = 0; i < shapes.size(); i++) {
if (shapes.get(i) != copies.get(i)) {
System.out.println(i + ": 不同对象的形状");
if (shapes.get(i).equals(copies.get(i))) {
System.out.println(i + ": 它们完全相同");
} else {
System.out.println(i + ": 它们并不完全相同");
}
} else {
System.out.println(i + ": 形状对象相同");
}
}
}
}Go
package main
import "fmt"
/**
* Node原型接口
*
*/
type Inode interface {
print(string)
clone() Inode
}
/**
* File具体原型
*
*/
type File struct {
name string
}
func (f *File) print(indentation string) {
fmt.Println(indentation + f.name)
}
func (f *File) clone() Inode {
return &File{name: f.name + "_clone"}
}
/**
* Folder具体原型
*
*/
type Folder struct {
children []Inode
name string
}
func (f *Folder) print(indentation string) {
fmt.Println(indentation + f.name)
for _, i := range f.children {
i.print(indentation + indentation)
}
}
func (f *Folder) clone() Inode {
cloneFolder := &Folder{name: f.name + "_clone"}
var tempChildren []Inode
for _, i := range f.children {
copy := i.clone()
tempChildren = append(tempChildren, copy)
}
cloneFolder.children = tempChildren
return cloneFolder
}
/**
* 客户端
*
*/
func main() {
file1 := &File{name: "File1"}
file2 := &File{name: "File2"}
file3 := &File{name: "File3"}
folder1 := &Folder{
children: []Inode{file1},
name: "Folder1",
}
folder2 := &Folder{
children: []Inode{folder1, file2, file3},
name: "Folder2",
}
fmt.Println("\n打印文件夹2大的继承结构")
folder2.print(" ")
cloneFolder := folder2.clone()
fmt.Println("\n打印克隆文件夹1的继承结构")
cloneFolder.print(" ")
}适用场景
当希望复制对象但又不想依赖于这些对象所属的类,或者无法知道它们的私有属性时就可以用原型模式。
如果子类之间的区别仅仅是其对象的初始化方式,可以用原型模式来减少子类的数量。
优缺点
原型模式的优点。
可以获得对象而无需与其所属的类耦合。
可以通过继承以外的方式来获得复杂对象。
原型模式的缺点。
对象必须实现克隆接口。
如果对象有循环引用处理起来比较麻烦。
相关性
当不需要链接其他外部资源,存储的对象状态比较简单时,
原型模式可以作为一个简化版本的备忘录模式。原型模式可用于保存命令模式的历史记录。抽象工厂模式通常基于一组工厂方法模式,但也可以使用原型模式来生成这些类的方法。工厂方法模式可以演化为抽象工厂模式、原型模式和构建器模式。可以通过
原型模式来控制组合模式和修饰器模式的复杂结构。抽象工厂模式、构建器模式和原型模式都可以用单例模式来实现。
感谢支持
更多内容,请移步《超级个体》。
