【设计模式-2.3】创建型——原型模式

说明:本文介绍设计模式中,创建型中的原型模式;

飞机大战

创建型设计模式关注于对象的创建,原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子,游戏中飞机、坦克对象会创建许许多多的实例,每个实例除了坐标,是一模一样的,如果每次都用关键字new去创建,是非常消耗时间的。

在这里插入图片描述

(Enemy,敌人抽象类)

/**
 * 敌人抽象类
 */
public abstract class Enemy {

    /**
     * 敌人的坐标
     */
    protected int x;

    /**
     * 敌人的坐标
     */
    protected int y;

    /**
     * 抽象方法
     */
    public Enemy(int x, int y) {
        this.x = x;
        this.y = y;
    }

    /**
     * 绘制方法
     */
    public abstract void show();
}

(AirPlane,飞机类)

/**
 * 飞机
 */
public class AirPlane extends Enemy {

    public AirPlane(int x, int y) {
        super(x, y);
    }

    @Override
    public void show() {
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }
}

(Client,客户端类,循环创建对象,浪费资源,影响效率

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 屏幕宽度
        int screenLength = 100;

        // 定义飞机集合
        List<AirPlane> airPlanes = new ArrayList<>();

        // 创建飞机
        for (int i = 0; i < 100000; i++) {
            airPlanes.add(new AirPlane(new Random().nextInt(screenLength), 0));
        }
    }
}

原型模式

原型模式,是使用了Object类里面的clone()方法,对原型对象,也就是创建的第一个对象进行了克隆,避免重复创建对象。如下:

(AirPlane,飞机类,实现Cloneable接口)

/**
 * 飞机
 */
public class AirPlane extends Enemy implements Cloneable {

    public AirPlane(int x, int y) {
        super(x, y);
    }
    
    @Override
    public void show() {
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /**
     * clone方法,调用父类的clone方法
     */
    public AirPlane clone() {
        Object obj = null;
        try {
            obj = super.clone();
            return (AirPlane) obj;
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆失败");
            return null;
        }
    }
}

(AirPlaneFactory,飞机工厂类,调用飞机的克隆方法)

/**
 * 飞机工厂
 */
public class AirPlaneFactory {

    /**
     * 加载一个原型对象
     */
    private static AirPlane airPlane = new AirPlane(0, 0);

    /**
     * 获取飞机
     * @param x
     * @return
     */
    public static AirPlane getAirPlane(int x) {
        AirPlane clone = airPlane.clone();
        clone.setX(x);
        return clone;
    }
}

(客户端类,创建飞机实例对象)

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 屏幕宽度
        int screenLength = 100;

        // 定义飞机集合
        List<AirPlane> airPlanes = new ArrayList<>();

        // 创建飞机
        for (int i = 0; i < 100000; i++) {
            airPlanes.add(AirPlaneFactory.getAirPlane(new Random().nextInt(screenLength)));
        }

        // 比较两个飞机是否相等
        System.out.println(airPlanes.get(0) == airPlanes.get(1));
    }
}

克隆的实例,是不相等的

在这里插入图片描述

以上就是通过原型模式实例化对象的过程,可以节约内存空间。

浅拷贝和深拷贝

在使用了clone()方法时,我们需要知道浅拷贝和深拷贝的概念。在Java中有基本数据类型和引用数据类型,其中基本数据类型有8种,分别是byte、short、int、long、float、double、char、boolean,除此之外的都是引用数据类型。

对于基本数据类型,浅拷贝和深拷贝都是创建一个新对象,而对于引用数据类型,浅拷贝是拷贝一个对象的指针,深拷贝是拷贝一个一模一样的对象

例如,在上面的基础上,我创建一个机长类,在飞机类里面注入一个机长类对象,克隆之后,对飞机里面的机长对象进行判断,如下:

(机长类)

/**
 * 机长
 */
public class Captain implements Cloneable{

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

(注入一个机长类对象)

/**
 * 飞机
 */
public class AirPlane extends Enemy implements Cloneable {

    /**
     * 机长
     */
    private Captain captain;

    public AirPlane(int x, int y, Captain captain) {
        super(x, y);
        this.captain = captain;
    }

    @Override
    public void show() {
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /**
     * clone方法,调用父类的clone方法
     */
    public AirPlane clone() {
        Object obj = null;
        try {
            obj = super.clone();
            return (AirPlane) obj;
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆失败");
            return null;
        }
    }

    public Captain getCaptain() {
        return captain;
    }
}

(克隆对象后,对飞机对象和飞机对象里面的机长对象进行判断)

	// 比较两个飞机是否相等
	System.out.println(airPlanes.get(0) == airPlanes.get(1));
	
	// 比较两个飞机的机长是否相等
	System.out.println(airPlanes.get(0).getCaptain() == airPlanes.get(1).getCaptain());

结果是飞机对象是互不相同的,但是飞机对象中的引用数据类型,是相同的,是浅拷贝;

在这里插入图片描述

这肯定是不行的,每个实例的数据应该是封装独有的,不能“克隆了,但没完全克隆”。我们可以采用字节流的方式实现“深拷贝”,如下:

(敌人抽象类)

/**
 * 敌人抽象类
 */
public abstract class Enemy {

    /**
     * 敌人的坐标
     */
    protected int x;

    /**
     * 敌人的坐标
     */
    protected int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    /**
     * 绘制方法
     */
    public abstract void show();
}

(飞机类,使用IO流的方式来实现深拷贝)

import java.io.*;

/**
 * 飞机
 */
public class AirPlane extends Enemy implements Serializable {

    /**
     * 机长
     */
    private Captain captain;

    public Captain getCaptain() {
        return captain;
    }

    public void setCaptain(Captain captain) {
        this.captain = captain;
    }

    @Override
    public void show() {
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /**
     * 获取飞机
     * @param x
     * @return
     */
    public AirPlane getAirPlane(int x) throws IOException, ClassNotFoundException {
        this.setX(x);

        // 创建对象输出流
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);

        // 返回克隆对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (AirPlane) ois.readObject();
    }
}

(机长类)

import java.io.Serializable;

/**
 * 机长
 */
public class Captain implements Serializable {

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

创建对象,调用“克隆”方法,比较两个对象的地址和对象内的引用类型数据的地址;

import java.io.IOException;
import java.util.Random;

/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 屏幕宽度
        int screenLength = 100;

        AirPlane airPlane = new AirPlane();

        airPlane.setCaptain(new Captain());

        AirPlane airPlaneNew = airPlane.getAirPlane(new Random().nextInt(screenLength));

        // 比较两个飞机是否相等
        System.out.println(airPlane == airPlaneNew);

        // 比较两个飞机的机长是否相等
        System.out.println(airPlane.getCaptain() == airPlaneNew.getCaptain());
    }
}

都为false,说明“深拷贝”已经实现。

在这里插入图片描述

深克隆的实现并未用到Object的clone方法,而是使用了IO流中的Object流的方式,对对象进行序列化来实现的。

原型管理器

原型管理器的构想是,创建一个管理器类,包含了所有需要克隆的对象,需要的时候可以直接调用管理器中的方法,取一个克隆的对象出来。代码如下:

(定义一个敌人接口,一个克隆方法,一个展示方法)

/**
 * 敌人接口
 */
public interface Enemy extends Cloneable{

    /**
     * 克隆方法
     * @return
     */
    Enemy clone();

    /**
     * 显示方法
     */
    void show();
}

(分别定义敌人飞机、敌人坦克对象)

/**
 * 敌人飞机
 */
public class AirPlane implements Enemy{

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

    @Override
    public void show() {
        System.out.println("敌人飞机");
    }
}

/**
 * 敌人坦克
 */
public class Tank implements Enemy{
    @Override
    public Enemy clone() {
        Tank tank = null;
        try {
            tank = (Tank) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("克隆失败");
        }
        return tank;
    }

    @Override
    public void show() {
        System.out.println("敌人坦克");
    }
}

(定义原型管理器,通过getKey()的方式获取到对应对象的克隆实例)

import java.util.Hashtable;

/**
 * 原型管理类
 */
public class PrototypeManagement {

    private Hashtable ht = new Hashtable();

    private static PrototypeManagement pm = new PrototypeManagement();

    /**
     * 添加敌人
     * @param key
     * @param enemy
     */
    public void addEnemy(String key, Enemy enemy) {
        ht.put(key, enemy);
    }

    /**
     * 获取敌人
     * @param key
     * @return
     */
    public Enemy getEnemy(String key) {
        Enemy enemy = (Enemy) ht.get(key);
        return enemy.clone();
    }

    public static PrototypeManagement getPrototypeManagement() {
        return pm;
    }
}

(客户端测试)

/**
 * 原型管理器客户端
 */
public class Client {
    public static void main(String[] args) {
        PrototypeManagement pm = PrototypeManagement.getPrototypeManagement();

        // 添加敌人
        pm.addEnemy("airplane", new AirPlane());
        pm.addEnemy("tank", new Tank());

        AirPlane airplane = (AirPlane) pm.getEnemy("airplane");
        airplane.show();

        AirPlane airplaneNew = (AirPlane) pm.getEnemy("airplane");
        airplaneNew.show();

        System.out.println(airplane == airplaneNew);

        System.out.println("====================================");

        Tank tank = (Tank) pm.getEnemy("tank");
        tank.show();

        Tank tankNew = (Tank) pm.getEnemy("tank");
        tankNew.show();

        System.out.println(tank == tankNew);
    }
}

通过克隆获取实例,地址不相等;

在这里插入图片描述

此时,如果需要增加一个Boss,非常简单,只需要新增一个类实现Enemy接口即可;

总结

本文参考《设计模式的艺术》、《秒懂设计模式》两书

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

水利安全监测方案——基于RTU200的解决方案

引言&#xff1a; 水资源是人类赖以生存的重要基础&#xff0c;对于保障水利系统安全运行以及应对自然灾害起着关键作用。为了实现水利安全监测的目标&#xff0c;我们提出了基于RTU200的解决方案。本方案将结合RTU200的可靠性、灵活性和高效性&#xff0c;为您打造一个全面的…

vue使用实现录音功能js-audio-recorder

前言 最近项目中需要实现一个录音上传功能&#xff0c;用于语音评论可以上录音。 下载插件&#xff1a; npm i js-audio-recorder完整代码 <template><div style"padding: 20px;"><h3>录音上传</h3><div style"font-size:14px"…

[Java学习日记]网络编程

目录 一.常见的软件架构、网络编程三要素、IP 二.利用UDP发送与接收数据 三.改聊天室 四.组播案例 五.TCP通信案例 一.常见的软件架构、网络编程三要素、IP 网络编程&#xff1a;在网络通信协议下&#xff0c;不同的计算机上运行的程序进行的数据传输 在Java中可以使用java…

Promise的resolve和reject方法(手写题)

1.resolve 2.reject 3.手写 1.resolve //构造函数上添加 resolve 方法 Promise.resolve function (value) {return new Promise((resolve, reject) > {if (value instanceof Promise) {value.then((val) > {resolve(val)},(err) > {reject(err)})} else {resolve(v…

springboot项目中注入bean后,调用时报n

需求&#xff1a; 在socket接收到上报数据后&#xff0c;在handler中调用工具类中ProtocolAnalyse的conAnalyse(byte[] data, int dataLen)解析数据。解析数据后&#xff0c;将解析后的结果保存至数据库。注入了三个bean&#xff1a; Autowiredprivate PersonTeService person…

基于融合正余弦和柯西变异的麻雀优化算法(SCSSA)-CNN-BiLSTM(双向长短期记忆网络)的时间序列预测模型

融合正余弦和柯西变异的麻雀优化算法&#xff08;SCSSA&#xff09;原理如下&#xff1a; ①采用折射反向学习策略初始化麻雀算法个体&#xff0c;基本思想是通过计算当前解的反向解来扩大搜索范围&#xff0c;借此找出给定问题更好的备选解&#xff1b;②采用正余弦策略替换原…

2023年建筑轮廓高度数据技术服务

在之前的文章【数据分享】全国62个城市建筑轮廓数据带层数&#xff08;2018年&#xff09;和【数据分享】全国77个城市建筑轮廓数据带高度&#xff08;2019年&#xff09;里我们分别分享了2018年和2019年2020年2021年2022年的建筑轮廓数据&#xff0c;数据年份也有些老了&#…

react实现加载动画

1.Spinning.tsx import "./Spinning.scss";interface Props {isLoading: boolean;children?: React.ReactNode; }const Spinning: React.FC<Props> ({isLoading true,children }) > {return <div className{spinning-wrapper${isLoading ? " l…

Hdoop学习笔记(HDP)-Part.2 核心组件原理

二、核心组件原理 1.分布式协调ZooKeeper (1)应用场景 使用分布式系统就无法避免对节点管理的问题&#xff08;需要实时感知节点的状态、对节点进行统一管理等&#xff09;&#xff0c;而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性&#xff0c;ZooKeeper作为一个…

5.C转python

新始: 13.列表可被改变(数据),元组不可被改变(数据),二者皆与C中的数组的大致相同 14.创建列表方法: 1.一个[ ]就是一个空的列表 2.使用list函数来创建列表 如: 15.可以在[ ]内部指定列表的初始值,打印方法: 如: 16.在python中,在同一个列表中,可以放不同类型的变量(也可…

【零基础入门Docker】Dockerfile中的USER指令以及dockerfile命令详解

✍面向读者&#xff1a;所有人 ✍所属专栏&#xff1a;Docker零基础入门专栏 目录 第 1 步&#xff1a;创建 Dockerfile 第 2 步&#xff1a;构建 Docker 镜像 第 3 步&#xff1a;运行 Docker 容器 第 4 步&#xff1a;验证输出 dockerfile命令详解 最佳实践 默认情况下…

oops-framework框架 之 初始了解(一)

引擎&#xff1a;CocosCreator 环境&#xff1a; Mac Gitee: oops-framework 简介 oops-framework是由作者dgflash编写&#xff0c;基于CocosCreator 3.x而实现的开源框架。 该框架以插件形式存在&#xff0c;主要目的是为了降低与项目的耦合&#xff0c;并且通过插件内部的…

【LeetCode:1657. 确定两个字符串是否接近 | 计数 + 阅读理解】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

若依前后端分离版idea启动

1.项目地址 https://gitee.com/y_project/RuoYi-Vue2.修改配置 修改数据库配置 3.执行sql 4.启动项目 启动成功 5.启动前端 首先要安装nodejs,版本不能太高,本人用的nodejs11版本的 npm install --registry https://registry.npm.taobao.orgnpm run dev启动成功 访问 h…

态路小课堂丨InfiniBand与以太网:AI时代的网络差异

近年来&#xff0c;随着人工智能技术的迅猛发展&#xff0c;以ChatGPT为代表的大型AI模型引起了广泛关注。然而&#xff0c;在这个AI时代&#xff0c;我们不得不重新审视传统的以太网和基于InfiniBand构建的网络之间的区别。尤其是今年以来&#xff0c;InfiniBand因其在AI领域中…

CRM:如何通过客户数据分析获得业务洞察?

客户数据分析&#xff0c;也称客户分析&#xff0c;是收集、组织和分析客户数据&#xff0c;以深入了解客户行为的过程。企业将利用这些数据来制定与营销、产品开发、销售等相关的业务决策。 通过客户分析&#xff0c;你可以做出简单的业务决策&#xff0c;比如找出投资回报率…

景联文科技数据标注平台助力AI数据实现价值最大化

随着人工智能技术不断进步&#xff0c;应用领域不断拓宽&#xff0c;对于高质量、大规模标注数据的需求也在不断增加。 数据标注是人工智能行业的基石。机器学习需要运用海量的有效数据来做支撑&#xff0c;而这些数据就需要我们的标注员对其进行分析和处理&#xff0c;想要得到…

【力扣周赛】第 369 场周赛(⭐记忆化搜索 树形DP)

文章目录 竞赛链接Q1&#xff1a;2917. 找出数组中的 K-or 值竞赛时代码——按题意模拟 Q2&#xff1a;2918. 数组的最小相等和竞赛时代码——分类讨论 Q3&#xff1a;2919. 使数组变美的最小增量运算数⭐⭐⭐竞赛时代码——动态规划解法2——记忆化搜索 翻译成递推 Q4&#xf…

HarmonyOS ArkTS与c++交互通信

一、创建Native C Module 1、右键项目->new->module 如图&#xff1a; 2、修改build-profile.json5配置 "externalNativeOptions": {"path": "./src/main/cpp/CMakeLists.txt","arguments": "-v -DOHOS_STLc_shared&quo…

带你用uniapp从零开发一个仿小米商场_10.开发一个占剩余窗口的滚动区域

首先是一个头部的tag切换栏,这个很简单,就不多说 源码奉上 <scroll-view scroll-x class"border scroll-row" style"height: 80rpx;"><view class"scroll-row-item" style"height: 80rpx;line-height: 80rpx;" v-for"(…