设计模式创建型——原型模式

目录

什么是原型模式

原型模式的实现

原型模式分类

浅克隆和深克隆的区别

原型模式浅克隆

原型模式深克隆

原型模式简单实现方式

原型模式登记实现方式

原型模式的特点

特点

优点

缺点

应用场景


什么是原型模式

             原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。

        原型模式简化了创建对象的过程,通过一个已有的实例进行复制提高了创建实例的效率,具有较好的可扩展性。

        通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone(),用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

原型模式的实现

原型模式分类

  1. 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
  2. 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅克隆和深克隆的区别

        使用浅克隆时,当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制;而使用深克隆时,除了对象本身被复制外,对象所包含的所有成员变量也将被复制。

        在java 中,浅克隆是继承Cloneable接口后重写clone方法完成的克隆,而深克隆需要继承序列化接口Serializable,使类可以序列化,然后将类写入流中再从流中取出完成克隆。

        下面我们用代码展示浅克隆和深克隆。

原型模式浅克隆

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 14:17:40
 */
public class Room implements Cloneable {
    public Room room;
    private String name;
    private int size;
    private String color;
    private String address = "上海市奉贤区奉城镇南奉公路686号4幢";

    public Room() {
    }

    public Room(String name, int size, String color) {
        super();
        this.name = name;
        this.size = size;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep [name=" + name + ", size=" + size + ", color=" + color + ", address=" + address + "]";
    }

    /**
     * 重写clone方法,或者用默认的clone方法也行,只不过调用时候需要抛异常
     */
    @Override
    protected Object clone() {
        Room room = null;
        try {
            room = (Room) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return room;
    }

    public static void main(String[] args) {
        System.out.println("原型模式完成对象的创建");
        Room room1 = new Room("昂焱数据", 1, "白色");
        room1.room = new Room("昂焱数据", 2, "黑色");
        Room room2 = (Room) room1.clone();
        Room room3 = (Room) room1.clone();

        System.out.println(room1.getColor().hashCode() == room2.getColor().hashCode());
        System.out.println(room1.getColor() == room2.getColor());

        System.out.println(room1.getName().hashCode() == room2.getName().hashCode());
        room1.room.setName("昂焱科技");
        System.out.println(room1.room.getName());
        System.out.println(room2.room.getName());
        System.out.println(room1.room.getName().hashCode() == room2.room.getName().hashCode());
        System.out.println(room1.room.getName() == room2.room.getName());

        System.out.println("room1.room =" + room1.room + "room1.room=" + room1.room.hashCode());
        System.out.println("room2.room =" + room2.room + "room2.room=" + room2.room.hashCode());
        System.out.println("room3.room =" + room3.room + "room3.room=" + room3.room.hashCode());

        System.out.println("room1 =" + room1 + "room=" + room1.hashCode());
        System.out.println("room2 =" + room2 + "room2=" + room2.hashCode());
        System.out.println("room3 =" + room3 + "room3=" + room3.hashCode());
    }
}

         从截图可以得知,原型类与克隆类两者对应的成员变量的内存地址是一样的,也就是说浅克隆时,并未对原型类中的成员变量进行克隆,具体原因是因为原型模式使用浅克隆时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并不会去复制。

原型模式深克隆
 

import java.io.*;

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 深克隆工具类 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 20:38:47
 */
public class CloneUtils {

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepClone(T obj) {
        T cloneObj = null;
        ObjectOutputStream obs = null;
        ObjectInputStream ois = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            obs = new ObjectOutputStream(out);
            obs.writeObject(obj);

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //PS:ByteArrayOutputStream和ByteArrayInputStream其实是伪装成流的字节数组
            // (把它们当成字节数据来看就好了),
            // 他们不会锁定任何文件句柄和端口,如果不再被使用,字节数组会被垃圾回收掉,所以不需要关闭。
            CloneUtils.close(obs);
            CloneUtils.close(ois);
        }
        return cloneObj;
    }

    /**
     * 关闭<br>
     * 关闭失败不会抛出异常
     *
     * @param closeable 被关闭的对象
     */
    public static void close(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (Exception e) {
                // 静默关闭
            }
        }
    }

}

import java.io.Serializable;

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 深克隆测试类 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 14:25:26
 */
public class Diary implements Serializable {
    private String author;
    private String content;
    private Diary diary;

    @Override
    public String toString() {
        return "Diary{" +
                "author='" + author + '\'' +
                ", content='" + content + '\'' +
                ", diary=" + diary +
                '}';
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Diary getDiary() {
        return diary;
    }

    public void setDiary(Diary diary) {
        this.diary = diary;
    }

    public Diary() {
    }

    public Diary(String author, String content) {
        this.author = author;
        this.content = content;
    }

    public Diary(String author, String content, Diary diary) {
        this.author = author;
        this.content = content;
        this.diary = diary;
    }
    
    public static void main(String[] args){
        Diary diary = new Diary("昂焱科技","深克隆测试");
        Diary diary1 = new Diary("昂焱数据","深克隆测试",diary);
        Diary diary2 = CloneUtils.deepClone(diary1);

        System.out.println(diary1.toString());
        System.out.println(diary2.toString());
        
        System.out.println(diary1.getDiary().hashCode() == diary2.getDiary().hashCode());
        System.out.println(diary1.hashCode() == diary2.hashCode());

    }

所有属性都是一份拷贝, 跟原数据不会有任何耦合(不存在引用共享) 

原型模式简单实现方式

简单实现方式包含三个角色:

抽象原型Prototype: 抽象原型类,给出所有的具体原型所需要实现的拷贝方法的接口或者抽象类。

具体原型ConcretePrototype: 用于拷贝的原型实例,实现了抽象原型Prototype所要求的接口。

客户Client: 客户端Client用来请求创建新的对象。

 抽象原型AbstratePrototype

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 形状抽象类 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 21:47:04
 */
public abstract class Shape implements Cloneable {

    private String id;
    protected String type;

    abstract void describe();

    public String getType(){
        return type;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

    @Override
    public String toString() {
        return "Shape{" +
                "id='" + id + '\'' +
                ", type='" + type + '\'' +
                '}';
    }
}

具体原型ConcretePrototype

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 形状原型具体实现类 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 21:49:49
 */
public class Circle extends Shape {

    public Circle(){
        type = "Circle";
    }

    @Override
    void describe() {
        System.out.println("describe : Circle ");
    }
}
public class Rectangle extends Shape {

    public Rectangle(){
        type = "Rectangle";
    }

    @Override
    void describe() {
        System.out.println("describe : Rectangle");
    }
}
public class Square extends Shape {

    public Square(){
        type = "Square";
    }

    @Override
    void describe() {
        System.out.println("describe : Square");
    }
}

客户Client

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 原型测试 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/11 09:28:54
 */
public class Client {

    public static void main(String[] args) {
        Square square = new Square();
        Square square1 = (Square) square.clone();
        System.out.println(square.toString() + " >>> " + square.hashCode());
        System.out.println(square1.toString() + " >>> " + square1.hashCode());
        
        Rectangle rectangle = new Rectangle();
        Rectangle rectangle1 = (Rectangle) rectangle.clone();
        System.out.println(rectangle.toString() + " >>> " + rectangle.hashCode());
        System.out.println(rectangle1.toString() + " >>> " + rectangle1.hashCode());

        Circle circle = new Circle();
        Circle circle1 = (Circle) circle.clone();
        System.out.println(circle.toString() + " >>> " + circle.hashCode());
        System.out.println(circle1.toString() + " >>> " + circle1.hashCode());
    }
}

原型模式登记实现方式

简单实现方式包含三个角色:

抽象原型Prototype:抽象原型类,给出所有的具体原型所需要实现的拷贝方法的接口或者抽象类。

具体原型ConcretePrototype:用于拷贝的原型实例,实现了抽象原型Prototype所要求的接口。

原型管理器PrototypeManager:原型管理器PrototypeManager类中使用HashMap保存多个复制的原型对象,供客户端Client类可以通过原型管理器中的get(String id) 方法获取复制的原型对象。

客户Client:客户端Client用来请求创建新的对象。

 抽象原型Prototype :同简单原型

具体原型ConcretePrototype :同简单原型

原型管理器PrototypeManager

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 形状原型管理器 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 21:52:52
 */
public class PrototypeManager {

    private static ConcurrentHashMap<String, Shape> shapeMap = new ConcurrentHashMap<>();

    /**
     * 根据形状id获取形状
     * @param shapeId 形状id
     * @return
     */
    public static Shape getShape(String shapeId) {
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape) cachedShape.clone();
    }
    
    /**
     * 对形状进行一个简单的初始化
     * shapeMap.put(shapeKey, shape);
     */
    public static void loadCache() {
        Circle circle = new Circle();
        circle.setId("1");
        shapeMap.put(circle.getId(), circle);

        Square square = new Square();
        square.setId("2");
        shapeMap.put(square.getId(), square);

        Rectangle rectangle = new Rectangle();
        rectangle.setId("3");
        shapeMap.put(rectangle.getId(), rectangle);
    }
}

客户Client

/**
 * @author Evan Walker
 * @version 1.0
 * @desc 原型测试 昂焱数据: https://www.ayshuju.com
 * @date 2023/04/10 21:51:32
 */
public class ClientTest {
    
    public static void main(String[] args) {
        PrototypeManager.loadCache();
        Shape clonedShape = PrototypeManager.getShape("1");
        System.out.println("Shape : " + clonedShape.getType());

        Shape clonedShape2 = PrototypeManager.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());

        Shape clonedShape3 = PrototypeManager.getShape("3");
        System.out.println("Shape : " + clonedShape3.getType());
    }
}

原型模式的特点

特点

  1. 原型模式属于创建型模式
  2. 原型模式要求原型类实现拷贝自身的接口,通过拷贝原型的实例来创建新的对象
  3. 原型模式拷贝原型实例创建的新对象不需要关心对象本身的类型,只需要实现原型实例拷贝自身的方法,通过自定义的拷贝方法来创建新的对象,不需要通过new创建新的对象。

优点

  1. 克隆对象, 而无需与它们所属的具体类相耦合。
  2. 克隆预生成原型, 避免反复运行初始化代码。
  3. 更方便地生成复杂对象。
  4. 用继承以外的方式来处理复杂对象的不同配置。

缺点

  1. 克隆包含循环引用的复杂对象可能会非常麻烦。
  2. 原型模式需要为每一个类配置一个clone() 方法。
  3. clone() 方法在每个类的内部,当对已有类进行重构时,需要修改代码,违背了开闭原则。

应用场景

  1. 当需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
  2. 当一个类初始化需要消耗很多资源时,有很多相似对象时,可以设计一个原型,通过对成员变量的些微修改来实现。
  3. 如果子类的区别仅在于其对象的初始化方式,当创建这些子类的目的是为了创建特定类型的对象时,可使用该模式来减少子类的数量。
  4. Spring中原型bean的创建,就是原型模式的应用。
  5. 可以用深克隆的方式保存对象的状态,比如实现撤销操作等。

更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值