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

news2024/11/16 1:53:13

原型模式(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图

这里我从软件设计师书本上摘抄到一个例子来介绍:
在这里插入图片描述
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;
    }
}

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

第二个实例实现原型模式

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

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图。
在这里插入图片描述
创建一个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 );
      }
      // 下面改为深克隆来完成对对象属性值 的复制.
   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/506042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux网络——Shell编程之循环

Linux网络——Shell编程之循环 一、循环1.循环与遍历2.循环的优势 二、for 循环语句1.for 语句的结构2.for语句应用示例 三、while 循环语句1.while 语句的结构2.while语句应用示例 四、until 循环五、跳出循环1.break2.continue3.exit 六、死循环 一、循环 定义&#xff1a;循…

2023年适合营销公司使用的十大「社交媒体管理」工具

在遍地都是数字营销公司的时代&#xff0c;对品牌来说&#xff0c;拥有强大的社交媒体影响力以保持竞争力从未如此重要。 而对于管理一个或多个品牌的数字营销公司来说&#xff0c;从内容创作到执行报告&#xff0c;使用正确的工具可以帮助你做到这一点。从规划、管理和跟踪社…

idb使用教程(一)

概述 iOS开发桥&#xff08;idb&#xff09;是一个多功能的工具&#xff0c;用于自动化iOS模拟器和设备。它在一个一致的、对人友好的界面中暴露了很多分布在苹果工具中的功能。 安装 idb由两个部分组成&#xff0c;每个部分都需要单独安装。 idb伴侣 每个目标&#xff08…

《花雕学AI》ChatMind:与AI对话,轻松梳理思路并创建思维导图

引言&#xff1a; 思维导图是一种有效的思维工具&#xff0c;可以帮助用户整理和表达自己的思路&#xff0c;提高学习和工作的效率和质量。然而&#xff0c;传统的思维导图工具往往需要用户花费大量的时间和精力&#xff0c;学习和操作复杂的界面和功能&#xff0c;而且很难根据…

医院智能导诊系统,医院导航解决方案

随着现代医院规模不断扩大&#xff0c;功能区域越来越细化&#xff0c;面对复杂的楼宇结构&#xff0c;集中的就诊人流&#xff0c;患者在就诊中经常会面临找不到目的地的困境&#xff0c;就诊体验变差。针对这个问题&#xff0c;一些面积和规模都比较大的医院&#xff0c;已经…

html实现经典打方块小游戏

文章目录 1.设计来源1.1 主界面1.2 游戏界面1.3 游戏结束界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130581985 html实现经典打方块小游戏源码 html实现经典打方块…

自主可控不走捷径,中国长城做难且正确的事

2020-2022年是中国信创产业的重要推广期&#xff0c;在国家战略的支持下&#xff0c;自主可控领域诸多相关企业均获得绝佳发展良机。 但信创产业“完成替代”不是终点&#xff0c;“实现领先”方是目标。如今势已启、路尚远&#xff0c;前景广阔的市场并不意味着自主可控相关企…

运营商三要素验证原理,这篇文章就够了!

引言 运营商三要素验证 API 是一种基于手机号码、身份证号码和姓名等三种信息的验证服务&#xff0c;主要用于验证用户身份信息的真实性和一致性&#xff0c;以及查询手机号码所属的运营商信息。 运营商三要素 API 的验证原理 1. 身份验证的原理 身份信息验证是运营商三要素…

入门级的家用洗地机怎么样?入门级洗地机推荐

洗地机的功能有很多&#xff0c;比如除菌、洗地机清洁地面的确是一把好手。但是&#xff01;清洁完之后还要手动清洗洗地机&#xff0c;是一件麻烦事啊&#xff01;现在市面上大部分洗地机都有自清洁这个功能&#xff0c;但是很多洗地机的自清洁并不算真正的自清洁&#xff0c;…

从月薪3000到月薪20000,自动化测试应该这样学...

绝大多数测试工程师都是从功能测试做起的&#xff0c;工作忙忙碌碌&#xff0c;每天在各种业务需求学习和点点中度过&#xff0c;过了好多年发现自己还只是一个功能测试工程师。 随着移动互联网的发展&#xff0c;从业人员能力的整体进步&#xff0c;软件测试需要具备的能力要…

倒转时空,颠覆传统:探究逆置链表的三种思路

本篇博客会讲解力扣“206. 反转链表”的解题思路&#xff0c;这是题目链接。 老规矩&#xff0c;先来审题&#xff1a; 示例如下&#xff1a; 提示和进阶&#xff1a; 本题的思路非常多&#xff0c;我讲解一下常见的思路。 思路1 最容易想到的方法&#xff0c;是直接使…

macOS本地python环境/vscode/导入python包/设置python解释器

查看macbook本地是否有python环境 输入python或者python3&#xff0c;退出python环境使用exit()&#xff0c;别忘了括号 没有的话去官网安装https://www.python.org/ 2. 安装vscode 官网https://code.visualstudio.com/ 3. 安装插件 点击左边的“插件”按钮&#xff0c;安装…

springcloud-gateway集成knife4j(swagger3)

springcloud-gateway集成knife4j&#xff08;swagger3&#xff09; springcloud-gateway集成knife4j&#xff08;swagger3&#xff09; 环境信息准备工作微服务集成knife4j 第一步&#xff1a;编写Knife4jApiInfoProperties第二步&#xff1a;编写配置类Knife4jConfig第三步&a…

5月9号软件资讯更新合集......

Linux 嵌入式系统构建工具 Yocto 发布 4.2 版本 基于 Linux 基金会的 Yocto 项目发布了 4.2 版本。Yocto 提供模板、工具和方法&#xff0c;帮助开发者创建基于 Linux 的定制版物联网 / 嵌入式操作系统&#xff0c;而无需关心硬件体系。 4.2 中的新功能 / 增强功能 Linux 内核…

106.(cesium篇)cesium椎体旋转

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <html lang="en"> <

【简单入门】ChatGPT prompt engineering (中文版)笔记 |吴恩达ChatGPT 提示工程

目录 思维导图一、资料二、 指南环境配置两个基本原则&#xff08;最重要!!!!&#xff09;原则一&#xff1a;编写清晰、具体的指令**策略一&#xff1a;使用分隔符清晰地表示输入的不同部分**&#xff0c;**分隔符可以是&#xff1a;&#xff0c;""&#xff0c;<…

camunda的Java委托如何使用

一、camunda的Java委托有什么用途 在Camunda中&#xff0c;Java委托是一种用于在流程执行期间执行自定义逻辑的Java类。使用Java委托&#xff0c;您可以在流程执行期间通过Java代码实现各种复杂的业务逻辑。 以下是一些使用Java委托的常见用途&#xff1a; 1、计算值&#x…

算法设计 || 第3题:多边形算法+分治算法解决循环赛问题(奇偶赛)

2022北京冬奥会在新冠疫情肆虐的情况下仍然成功举办&#xff0c;体现了我们国家和组织者的集体智慧。假设有项体育比赛有N14个队伍需要和其他N-113只队伍进行循环赛, 如果偶数个队伍&#xff0c;每个队伍每天捉对赛一场&#xff0c;共进行N-113天比赛; 奇数个队伍会出现每天有…

【K8s】Pod一文详解

文章目录 一、Pod介绍1、Pod结构2、Pod的定义 二、Pod配置&#xff1a;spec.containers1、基本配置 name和image2、镜像拉取策略 imagePullpolicy3、启动命令 command4、环境变量 env5、端口设置 ports6、资源配额 resources 三、Pod的生命周期1、创建和终止2、初始化容器3、钩…

Docker 部署 Zabbix6.4

一、安装docker 1.1.离线安装docker docker网址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/ [rootVM-16-15-centos ~]# mkdir docker_install [rootVM-16-15-centos ~]# cd docker_install/ [rootVM-16-15-centos docker_install]# vim docker.se…