原型模式
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。 不需要知道任何创建的细节,不调用构造函数.
适用场景
类初始化消耗较多资源
new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
构造函数比较复杂
循环体中生产大量的对象时
优点
比直接new一个对象性能高
简化创建过程
缺点
必须配备克隆方法
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
深拷贝、浅拷贝要运用得当
Golang Demo
package prototype
// 原型对象需要实现的接口
type Cloneable interface {
Clone() Cloneable
}
type User struct {
name string
age int
}
func (u User) Clone() Cloneable {
return u
}
package prototype
import (
"fmt"
"strconv"
"testing"
)
func Test(t *testing.T) {
user := User{}
user.name = "init"
usermap := make(map[string]User)
for i := 1; i < 10; i++ {
u := user.Clone().(User)
u.name = strconv.Itoa(i)
u.age = i
usermap[strconv.Itoa(i)] = u
}
fmt.Println(user)
fmt.Println(usermap)
}
由于golang中引入了指针,所以很巧的是,如果在返回值时,不指明是指针引用的话,就是值拷贝,因此原型模式的应用在golang中并不能得到很好的体现。也有可能是笔者学习不到位,后期会进行补充。
下面给出github上的一种实现方式。https://github.com/senghoo/golang-design-pattern/blob/master/07_prototype/prototype.go
package prototype
//Cloneable 是原型对象需要实现的接口
type Cloneable interface {
Clone() Cloneable
}
type PrototypeManager struct {
prototypes map[string]Cloneable
}
func NewPrototypeManager() *PrototypeManager {
return &PrototypeManager{
prototypes: make(map[string]Cloneable),
}
}
func (p *PrototypeManager) Get(name string) Cloneable {
return p.prototypes[name]
}
func (p *PrototypeManager) Set(name string, prototype Cloneable) {
p.prototypes[name] = prototype
}
package prototype
import "testing"
var manager *PrototypeManager
type Type1 struct {
name string
}
func (t *Type1) Clone() Cloneable {
tc := *t
return &tc
}
type Type2 struct {
name string
}
func (t *Type2) Clone() Cloneable {
tc := *t
return &tc
}
func TestClone(t *testing.T) {
t1 := manager.Get("t1")
t2 := t1.Clone()
if t1 == t2 {
t.Fatal("error! get clone not working")
}
}
func TestCloneFromManager(t *testing.T) {
c := manager.Get("t1").Clone()
t1 := c.(*Type1)
if t1.name != "type1" {
t.Fatal("error")
}
}
func init() {
manager = NewPrototypeManager()
t1 := &Type1{
name: "type1",
}
manager.Set("t1", t1)
}
Java Demo
浅拷贝,clone 的时候,并不会调用构造器。浅克隆默认引用的是同一个对象,这样是会有隐患的。
package tech.selinux.design.pattern.creational.prototype;
public class Mail implements Cloneable {
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mail{"
+ "name='"
+ name
+ '\''
+ ", emailAddress='"
+ emailAddress
+ '\''
+ ", content='"
+ content
+ '\''
+ '}'
+ super.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("clone mail object");
return super.clone();
}
}
package tech.selinux.design.pattern.creational.prototype;
import java.text.MessageFormat;
public class MailUtil {
public static void sendMail(Mail mail) {
String outputContent = "向{0}用户,邮件地址:{1},邮件内容:{2}发送邮件成功";
System.out.println(
MessageFormat.format(
outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()));
}
public static void saveOriginMailRecord(Mail mail) {
System.out.println("存储originMail记录,originMail:" + mail.getContent());
}
}
package tech.selinux.design.pattern.creational.prototype;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("初始化模板");
System.out.println("初始化mail:" + mail);
for (int i = 0; i < 10; i++) {
Mail mailTemp = (Mail) mail.clone();
mailTemp.setName("姓名" + i);
mailTemp.setEmailAddress("姓名" + i + "@selinux.tech");
mailTemp.setContent("恭喜您,此次中奖了");
MailUtil.sendMail(mailTemp);
System.out.println("克隆的mailTemp:" + mailTemp);
}
MailUtil.saveOriginMailRecord(mail);
}
}
深克隆,深克隆也需要对clone方法进行重写。对于引用类型一定要注意是否需要深克隆。
package tech.selinux.design.pattern.creational.prototype.clone;
import java.util.Date;
public class User implements Cloneable {
private String name;
private Date birthday;
public User(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
User user = (User) super.clone();
// 深克隆
user.birthday = (Date) user.birthday.clone();
return user;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", birthday=" + birthday + '}' + super.toString();
}
}
package tech.selinux.design.pattern.creational.prototype.clone;
import java.util.Date;
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Date birthday = new Date(0L);
User user = new User("佩奇", birthday);
User user1 = (User) user.clone();
System.out.println(user);
System.out.println(user1);
user.getBirthday().setTime(666666666666L);
System.out.println(user);
System.out.println(user1);
}
}
通过抽象类的方式实现原型,如果实际业务中能够进行合理的抽象的话,可以使用下面的方式。
package tech.selinux.design.pattern.creational.prototype.abstractprototype;
public abstract class A implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package tech.selinux.design.pattern.creational.prototype.abstractprototype;
public class B extends A {
public static void main(String[] args) throws CloneNotSupportedException {
B b = new B();
b.clone();
}
}
补充另一个版本的Java/Scala Demo 以及源码解析
Java Demo_
Scala Demo
UML_
源码解析
Last updated
Was this helpful?