设计模式之原型模式

写在前面

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

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>();@Overridepublic 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)详解及代码示例 。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云计算——云计算与虚拟化的关系

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 一.虚拟化 1.什么是虚拟化 2.虚拟化技术作用 二.云计算与虚拟化的关系 三.虚…

ThunderScope开源示波器

简介 4CH&#xff0c;1GSa/S 开源示波器。前端很简洁&#xff0c;BUF802LMH6518&#xff0c;ADC是HMCAD1511&#xff0c;用Xilinx A7 FPGA进行控制&#xff0c;数据通过PCIE总线传输到上位机处理。目前这个项目已经被挂到了Xilinx官网&#xff0c;强。 设计日志&#xff1a;h…

【Docker】Docker镜像和Docker容器

文章目录 Docker镜像镜像基本概念为什么需要镜像&#xff1f;Union FS&#xff08;联合文件挂载&#xff09;docker镜像原理 Docker镜像命令docker rmidocker savedocker loaddocker historydocker image prune docker镜像实战离线迁移镜像镜像存储的压缩与共享 Docker容器容器…

Spring设计模式及部分技术讲解

讲师:邓澎波 Spring面试专题 1.Spring应该很熟悉吧?来介绍下你的Spring的理解 有些同学可能会抢答,不熟悉!!! 好了,不开玩笑,面对这个问题我们应该怎么来回答呢?我们给大家梳理这个几个维度来回答 1.1 Spring的发展历程 先介绍Spring是怎么来的,发展中有哪些核心的节…

mysql 2 -- 数据库基本操作、数据表的操作、mysql查询操作

一、数据库基本操作 1、数据库的登录及退出 连接数据库&#xff1a; mysql -u用户名 -h主机地址(省略代表本机) -p 密码&#xff08;格式为123...&#xff09;;注&#xff1a; 刚下载安装的时候需要通过管理员进入 退出数据库,以下三种方式都可以&#xff1a; exit quit …

大数据学习02-Hadoop分布式集群部署

操作系统&#xff1a;centos7 软件环境&#xff1a;jdk8、hadoop-2.8.5 一、创建虚拟机 1.下载VMware,建议支持正版 2.安装到Widows目录下任意位置即可&#xff0c;安装目录自定义。打开VMware&#xff0c;界面如下&#xff1a; 3.创建虚拟机 创建虚拟机—>选择自定义 …

android 下载源码 一路踩坑

python 从 2.0 升级到3.0 从官网下载 pyhon3.0 安装器,然后更改配置 # Setting PATH for Python 3.8# The original version is saved in .bash_profile.pysaveexport PATH"/Library/Frameworks/Python.framework/Versions/3.11/bin:$PATH"alias python"/Libr…

SpringCloud(四)Hystrix服务降级、熔断、监控页面

一、服务熔断 官方文档&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients 我们知道&#xff0c;微服务之间是可以进行相互调用的&#xff0c;那么如果出现了…

如何创建 Spring Boot 项目

目录 一、Spring Boot 项目的创建 1. 安装 Spring Boot Helper 插件 2. 创建 Spring Boot 项目 3. 项目目录详解 4. 运行 Spring Boot 项目 二、Spring Boot 的优点 前言 Spring Boot 的意思就是 Spring Boot 脚手架的意思&#xff0c;已经总结完成 Spring 的学习&#x…

js计算数组中每个元素出现的次数

tip&#xff1a;空值合并运算符&#xff08;??&#xff09;是一个逻辑运算符&#xff0c;当左侧的操作数为 null 或者 undefined 时&#xff0c;返回其右侧操作数&#xff0c;否则返回左侧操作数。reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数&#xff0…

华为鲲鹏920 aarch64 版本 Ambari HDP 下载地址

声明&#xff1a;为有效缓解各位同行兄弟们的痛&#xff0c;特推出此文 本文能够适配兼容 华为鲲鹏920 aarch64 版本&#xff0c;仅限 CentOS7、openEuler20.03-LTS 操作系统 以下是详细下载地址 1、CentOS7 aarch64版本 CentOS7 aarch64 https://mirrors.huaweicloud.com/…

【HCIA】10.VLAN间通信

VLAN间通信的解决方法 使用路由器的物理接口 路由器三层接口作为网关&#xff0c;转发本网段前往其它网段的流量。路由器三层接口无法处理携带VLAN Tag的数据帧&#xff0c;因此交换机上联路由器的接口需配置为Access。路由器的一个物理接口作为一个VLAN的网关&#xff0c;因此…