二十三种设计模式第五篇--原型模式
  cEe6YWJIAuf2 2023年11月05日 15 0


原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。
我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

用原型实例来指定创建对的种类,并且通过拷贝这些原型来创建新的对象。

在什么情况下使用原型模式:

  1. 在类的初始化消耗的资源非常之多的时候使用
  2. 使用new创建一个对象需要非常烦琐的过程
  3. 构造函数比较复杂
  4. 在循环体中产生大量对象

注意原型模式中有使用克隆操作来实现复制类,但是clone操作又可以分为深克隆和浅克隆之分,既然要用到clone操作,我们知道深拷贝需要实现Cloneable,Serializable两个接口,重写clone方法,并且在java中,我们主要使用的clone操作都是浅拷贝。
其深拷贝具体方法:

  1. 实现 Cloneable 接口,递归 clone 引用对象或 new 新对象(类的属性字段未实现 Cloneable 接口)
  2. 借助序列化完成深拷贝,如实现 JDK java.io.Serializable 接口、json格式序列化、xml格式序列化等。

原型模式UML图

这里我从软件设计师书本上摘抄到一个例子来介绍:

二十三种设计模式第五篇--原型模式_ide


Prototype模式适用于:

  • 当一个系统应该独立于它的产品创建、构成和表示时。
  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
  • 为了避免创建一个与产品类层次平行的工厂类层次时。
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。

原型模式代码

根据上述UML图,我们来具体实现:

public class Main {
    public static void main(String[] args) {
        Product product1 = new Product(2023, 5.8);
        System.out.println(product1.getId() + " " + product1.getPrice());

        // Product product2 = new Product(2023, 5.8);
        Product product2 = (Product) product1.Clone();
        System.out.println(product2.getId() + " " + product2.getPrice());

        Product product3 = (Product) product1.Clone();
        System.out.println(product3.getId() + " " + product3.getPrice());
    }
}
interface Prototype {
    public Object Clone();
}
class Product implements Prototype {
    private int id;
    private double price;

//创建无参构造方法
    public Product() {}

//有参构造
    public Product(int id, double price) {
        this.id = id;
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public Object Clone() {
        Product object = new Product();
        object.id = this.id;
        object.price = this.price;

        return object;
    }
}

通过上述方式,我们就已经简单实现一个原型模式了。

第二个实例实现原型模式

二十三种设计模式第五篇--原型模式_设计模式_02


这个图应该看得懂吧?孙大圣以及金箍棒,首先孙悟空是猴子,他有个绰号叫齐天大圣,我们在齐天大圣里边实现原型模式。

public class Monkey {
    public int height;
    public int weight;   //基本型数据
    public Date birthday;

}

创建金箍棒,实现序列化接口

//金箍棒
public class JinGuBang implements Serializable {
    public float h = 100;
    public float d = 10;

//金箍棒变大
    public void big(){
        this.d *= 2;
        this.h *= 2;
    }
//金箍棒变小
    public void small(){
        this.d /= 2;
        this.h /= 2;
    }

    @Override
    public String toString() {
        return "JinGuBang{" +
                "h=" + h +
                ", d=" + d +
                '}';
    }
}
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {

    public JinGuBang jinGuBang;

    Object obj;

//先初始化一些数值
    public  QiTianDaSheng(){
        //只是初始化
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

    //将原来的浅克隆改为深克隆.
    @Override
    protected Object clone() throws CloneNotSupportedException {
      //  return super.clone();
      // return this.shallowClone(this);   //浅克隆
        return this.deepClone();   //深克隆
    }

    //利用对象流完成深克隆,       还有一种麻烦的实现: 递 归.
    /*
        深复制把要复制的对象所引用的对象都复制了一遍。
        拷贝需要实现Cloneable, Serializable两个接口,重写clone方法
     */
    public Object deepClone(){
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            oos.flush();

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
            copy.birthday = new Date();
            return copy;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }

    }

    //浅克隆
    public QiTianDaSheng shallowClone(QiTianDaSheng target){
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;
        //浅克隆对对象类型的数据只克隆了地址,没有复制值
        qiTianDaSheng.jinGuBang = target.jinGuBang;    //对于引用型的数据,只克隆了地址.
        qiTianDaSheng.birthday = new Date();
        return  qiTianDaSheng;
    }

    @Override
    public String toString() {
        return "QiTianDaSheng{" +
                "jinGuBang=" + jinGuBang +
                ", height=" + height +
                ", weight=" + weight +
                ", birthday=" + birthday +
                '}';
    }
}
public class DeepCloneTest {

    public static void main(String[] args) {

        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();

        try {
            QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
            System.out.println(   "克隆:"+   clone);
            System.out.println(     qiTianDaSheng );
            //原型模式下对象创建了两次,但值 一样.
            System.out.println(   "hashcode:  " +  clone.hashCode()+"\t"+ qiTianDaSheng.hashCode() );
            //jingubang是一个引用型, 对于浅克隆.
            System.out.println(  "jingubang的hashcode: "+ clone.jinGuBang.hashCode() +"\t"+   qiTianDaSheng.jinGuBang.hashCode() );


          //  System.out.println("判断克隆结果,如两个jinGuBang对象相同,则为浅克隆,如不同,则为深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
        } catch (Exception e) {
            e.printStackTrace();
        }

//        QiTianDaSheng q = new QiTianDaSheng();
//        QiTianDaSheng n = q.shallowClone(q);
//        System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
    }
}

至此,我们通过齐天大圣类来完成类的克隆,并且实现序列化接口和Cloneable接口,完成深克隆操作,来实现原型模式。如果以上介绍还不是看的很爽,那我们接着看。

第三个实例实现原型模式

老样子,先摆上UML图。

二十三种设计模式第五篇--原型模式_ide_03


创建一个shape类作为父类

public abstract class Shape implements Cloneable {
   
   private String id;
   protected String type;

   public List values;

   //注意关键是在 这里,应该重写一个clone()方法,提供对象克隆功能。默认是一个浅克隆.
   @Override
   public Object clone() {
      Object clone = null;
      try {
         clone = super.clone();    ///
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
   
   abstract void draw();

   public void setValues(List values) {
      this.values = values;
   }

   public List getValues() {
      return values;
   }

   public String getType(){
      return type;
   }
   
   public String getId() {
      return id;
   }
   
   public void setId(String id) {
      this.id = id;
   }
}

创建子类

public class Circle extends Shape implements Cloneable{
 
   public Circle(){
     type = "Circle";
   }
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}
public class Rectangle extends Shape {
 
   public Rectangle(){
     type = "Rectangle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
public class Square extends Shape {
 
   public Square(){
     type = "Square";
   }
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

创建好克隆类

public class ShapeCache {
   //用一个容器存好原始对象.
   private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();

   //如果要获取某个对象的一个拷贝,则从容器中取出原型, 再调用原型的拷贝方法
   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");
      List list=new ArrayList();
      list.add(1);
      list.add(2);
      circle.setValues(  list   );
      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);
   }
}

创建测试类

public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();  //首先加载 类的原型实例.


      //以下操作获取原型的克隆对象.
      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      //在这里可以对这个对象进行值 的设置,想像一下,如果这个对象有多个值 要设置的话,避免了创建对象的消耗.
      System.out.println("Shape : " + clonedShape.getType()+"  "+clonedShape);

      Shape cs2 = (Shape) ShapeCache.getShape("1");
      //在这里可以对这个对象进行值 的设置,想像一下,如果这个对象有多个值 要设置的话,避免了创建对象的消耗.
      cs2.setId("333");
      // *******注意两次获取的对象不同,这叫原型模式
      System.out.println("Shape : " + cs2.getType()+"   "+cs2 );

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

      //默认情况下: clone()是一个浅克隆.
      System.out.println(  "两次产生的对象中的 引用类型的属性值(地址) 是相等的:"+  ( clonedShape.values==cs2.values )  );
      //这说明: 当对clonedShape中的 values进行修改时, cs2.values也会变,这肯定是不被 允许 .
      clonedShape.values.add("abc");  //  cs2.values也会变
      for(   Object o:cs2.values){
         System.out.println(   o );
      }
      // 下面改为深克隆来完成对对象属性值 的复制.
   }
}


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  anLrwkgbyYZS   2023年12月30日   33   0   0 ideciciMaxideMax
cEe6YWJIAuf2