淘先锋技术网

首页 1 2 3 4 5 6 7

写在前面

但我们看某个电影,或者是某个电视剧的时候,总会提到某某人是某某角色的原型,这里某某角色就好像是某某人的复制品一样,这里的原型设计模式也是如此,不过,这里的原型是一个对象,而原型设计模式就是指复制这个原型对象,生成一个新的对象。本文就一起来看下吧!

1:介绍

1.1:什么时候使用原型设计模式

当对象的创建成本比较大,比如一个对象的创建需要依赖于其它服务的调用,需要依赖于数据库的查询,则此时,就可以考虑使用原型设计模式,通过拷贝(克隆)的方式来生成新的对象。实际上,在java中,原型设计模式的实现是非常简单的,因为java天生支持克隆,只需要实现java.lang.Clonable接口标记该对象可以被克隆,则就可以调用clone方法(来自Object的native方法)来克隆对象了。

1.2:UML类图

原型设计模式,包含如下元素:

1:抽象原型类
    提供一个克隆方法的接口,在java中就是java.lang.Clonable
2:具体原型类
    实现抽象原型类,并实现其克隆方法,完成克隆
3:客户端类
    使用具体原型类的克隆方法完成对象的克隆,获取新对象

UML图如下:

在这里插入图片描述

2:实例

源码

2.1:场景

假定我们现在有了一个经过非常复杂的逻辑,经过数据库查询才创建出来的一个对象,并且该对象的创建每次都是如此,这里我们就是用原型设计模式来创建新的对象,避免性能损耗,以及提高对象创建的速度。

2.2:程序

  • 抽象原型类
    即java.lang.Clonable,不需要我们额外编写了,如下:
public interface Cloneable {
}
  • 具体原型类
public class Thing2 implements Cloneable {
    private ArrayList<String> list = new ArrayList<String>();

    @Override
    public Thing2 clone() {
        Thing2 thing = null;
        try {
            thing = (Thing2) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            System.out.println("克隆失败");
        }
        return thing;
    }

    public void setValue(String value) {
        this.list.add(value);
    }

    public ArrayList getValue() {
        return this.list;
    }
}
  • 客户端类
public class SimpleClone {
    public static void main(String[] args) {
        Thing2 thing = new Thing2();
        thing.setValue("张三");
        Thing2 cloneThing = thing.clone();
        cloneThing.setValue("李四");
        System.out.println("原始对象:" + thing.getValue());
        System.out.println("拷贝对象:" +cloneThing.getValue());
    }
}

运行:

原始对象:[张三, 李四]
拷贝对象:[张三, 李四]

注意到,拷贝出来的对象,添加了李四,也影响了原始的对象,出现这个问题的原因是java的克隆是浅拷贝,即对象类型和数组类型是不拷贝的,而这里的List就是对象类型,这里其实是个坑,很容易掉进去,因此我们要用深拷贝,做法也比较简单,直接对对象类型再拷贝重新赋值到克隆对象即可,修改clone方法如下:

@Override
public Thing1 clone() {
    Thing1 thing = null;
    try {
        thing = (Thing1) super.clone();
        thing.list = (ArrayList) this.list.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
        System.out.println("克隆失败");
    }
    return thing;
}

thing.list = (ArrayList) this.list.clone();这行代码就完成了深拷贝,此时再运行,结果如下:

原始对象:[张三]
拷贝对象:[张三, 李四]

3:原型设计模式变种

当具体原型对象,不是一个,而是多个时,为了方便获取克隆的原型对象,增加了原型管理器的角色,客户端类不需要自己调用克隆方法生成对象,而是通过原型管理器来获取克隆的对象,如下图:

在这里插入图片描述
先来定义两个具体产品类(有接口):

public interface Shape extends Cloneable {
    public Object clone();    //拷贝

    public void countArea();    //计算面积
}

public class Circle implements Shape {
    public Object clone() {
        Circle w = null;
        try {
            w = (Circle) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("拷贝圆失败!");
        }
        return w;
    }

    public void countArea() {
        int r = 0;
        System.out.print("这是一个圆,请输入圆的半径:");
        Scanner input = new Scanner(System.in);
        r = input.nextInt();
        System.out.println("该圆的面积=" + 3.1415 * r * r + "\n");
    }
}

public class Square implements Shape {
    public Object clone() {
        Square b = null;
        try {
            b = (Square) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("拷贝正方形失败!");
        }
        return b;
    }

    public void countArea() {
        int a = 0;
        System.out.print("这是一个正方形,请输入它的边长:");
        Scanner input = new Scanner(System.in);
        a = input.nextInt();
        System.out.println("该正方形的面积=" + a * a + "\n");
    }
}

定义原型管理器类(有点工厂的意思了)

public class ProtoTypeManager {
    private HashMap<String, Shape> ht = new HashMap<String, Shape>();

    public ProtoTypeManager() {
        ht.put("Circle", new Circle());
        ht.put("Square", new Square());
    }

    public void addshape(String key, Shape obj) {
        ht.put(key, obj);
    }

    public Shape getShape(String key) {
        Shape temp = ht.get(key);
        // 每次克隆新的,但是注意,这里是浅拷贝,实际中还是深拷贝,避免出现多个对象的引用类型属性指向同一个对象的情况
        return (Shape) temp.clone();
    }
}

测试:

public class ProtoTypeShape {
    public static void main(String[] args) {
        ProtoTypeManager pm = new ProtoTypeManager();
        Shape obj1 = (Circle) pm.getShape("Circle");
        obj1.countArea();
        Shape obj2 = (Shape) pm.getShape("Square");
        obj2.countArea();
    }
}

输出:

这是一个圆,请输入圆的半径:该圆的面积=28.2735
这是一个正方形,请输入它的边长:该正方形的面积=81

写在后面

参考文章列表

设计模式之原型模式(Prototype)详解及代码示例