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

news2024/12/27 11:33:31

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

飞机大战

创建型设计模式关注于对象的创建,原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子,游戏中飞机、坦克对象会创建许许多多的实例,每个实例除了坐标,是一模一样的,如果每次都用关键字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接口即可;

总结

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

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

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

相关文章

【AB平台数据建设】从实验平台到数据管道

文章目录 前言1.从AB实验平台聊起(1)AB平台在业务中的发挥那些作用(2)AB平台进行实验工作流介绍 2.实验平台底层数据管道最小MVP解构(1)数据管道数据从哪里来&#xff1f;(2)数据管道的输出数据有哪些&#xff1f; 小结 前言 AB实验平台是一种通过小范围放量&#xff0c;测试不…

leecode 【二】

相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返…

春秋云镜ED01-CMS v20180505 存在任意文件上传漏洞

靶场介绍 春秋云镜ED01-CMS v20180505 存在任意文件上传漏洞 漏洞分析&#xff1a; 文件类型未校验可以任意上传执行文件&#xff0c;获取服务器权限 登录注册界面 Hi-Lo-Yohttp://eci-2ze2qm1cbaon2lylin0q.cloudeci1.ichunqiu.com/registration.php 注册了几个发现注册不…

虚幻学习笔记8—蓝图操作其他虚幻模块

一、前言 蓝图不仅可以相互之间操作和通信&#xff0c;其他的资源、模块也有操作和通信的方法。文本主要针对蓝图和材质、Niagara、编辑器的通信进行讲解。 二、实现 2.1、蓝图和材质 1&#xff09;首先&#xff0c;在材质蓝图中按住“4鼠标左键”创建一个参数为四维向量的参…

TZOJ 1402 Bitset

答案&#xff1a; #include <stdio.h> int main() {int n 0, j 0; while (scanf("%d", &n) ! EOF && (n>0 && n<1000)) //多组输入{int arr[32], i 0;while (n > 0) {arr[i] n % 2; //除2取余法n / 2;}for (j i -…

基于springboot+vue的秒杀商城(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Java全栈基础篇--集合

集合 集合&#xff1a;集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 特点&#xff1a; 长度不固定&#xff0c;还可以存储不同的数据&#xff08;但是一般都用同一类型&#xff09; 集合和数组既然都是容器&#xff0c;它们有啥区别呢&#xff1f; 数组的长…

rust持续学习 COW

COW我第一次看见还以为是奶牛 很奇怪是个啥 后来了解到是clone on write 缩写的&#xff0c;大乌龙啊 这个有两种enum,一种是borrow&#xff0c;一种是own rust中&#xff0c;数据读写经常涉及到所有权 这个borrow&#xff0c;很显然&#xff0c;就是不可变借用了 own就是可以写…

稻盛和夫:毕生经验总结出的36条管理经验,总有一条能戳中你。

大家好&#xff0c;我是老原。 进入职场&#xff0c;每个道理在每个人身上都有不同的理解。 大家经理不同&#xff0c;血泪自然不同&#xff0c;毕竟人类的悲喜并不相通&#xff0c;只有总结下来的经验才最有用。 我平时给你们分享的硬干货比较多&#xff0c;这种软道理写的…

【软件推荐】卸载360软件geek;护眼软件flux;

卸载360软件geek f.lux: software to make your life better (justgetflux.com) 卸载完扫描残留 护眼软件 hf.lux: software to make your life better (justgetflux.com)https://justgetflux.com/https://justgetflux.com/

源码安装mysql

使用源码安装mysql&#xff0c;这里选择的版本是mysql5.7.35 ,系统是Centos7.6 官网下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 下载源码压缩包 [rootlocalhost ~]# cd /opt[rootlocalhost opt]# wget https://downloads.mysql.com/archives/get/…

冒个泡!OceanBase亮相 2023 新加坡金融科技节

近日&#xff0c;OceanBase 亮相 Singapore Fintech Festival 2023&#xff08;2023 新加坡金融科技节&#xff09;&#xff01;本届新加坡金融科技节于 2023 年 11 月 15 日至 17 日在新加坡博览展览中心举行&#xff0c;展会期间&#xff0c;OceanBase 得到了众多金融科技机构…

WiFi概念介绍

WiFi概念介绍 1. 什么是WLAN2. 什么是Wi-Fi3. Wi-Fi联盟4. WLAN定义范围5. WiFi协议体系6. 协议架构7. WiFi技术的发展7.1 IEEE802.117.2 802.11标准和补充 8. 术语 1. 什么是WLAN Wireless Local Area Network&#xff0c;采用802.11无线技术进行互连的一组计算机和相关设备。…

windows电脑申请hbuilderx打包证书

hbuilderx现在的最新框架uniapp&#xff0c;可以多端开发不同端的应用&#xff0c;包括小程序、公众号、ios app和安卓app&#xff0c;一份代码就可以生成不同的端。 但是不同的端是需要使用不同的端的证书的&#xff0c;是不能使用公共的证书上架的&#xff0c;必须使用自己的…

adb环境搭建(adb下载与安装)

文章目录 前言一、adb下载二、adb安装1.将下载的安装包解压缩2.将解压缩后的文件夹放到自己想存放的目录下&#xff08;不要放到带有中文的目录下&#xff09;——我这放到D盘根目录下3.配置环境变量3.1.鼠标放到 "此电脑"→鼠标右击→选择属性3.2.点击 "高级系…

Git常用命令#更改用户名

1.查看当前用户信息 git config user.name2.更改用户名 特定仓库中更改用户名 如果你只想在特定仓库中更改用户名&#xff0c;可以在不使用 --global 参数的情况下执行相同的命令&#xff0c;并进入特定仓库的目录进行修改。 cd /path/to/your/repository git config user.na…

0X04

看到一道有趣的misc题 misc签到题 打开后啥都没有&#xff0c;全选后发现每一行有空格&#xff0c;数了一行发现空格数量转ascil码后是f&#xff0c;猜测都如此&#xff0c; 后面就可以交个脚本了&#xff0c;统计之后转换成ascii from Crypto.Util.number import long_to_b…

Android Bitmap 使用Vukan、RenderEffect、GLSL实现模糊

文章目录 Android Bitmap 使用Vukan、RenderEffect、GLSL实现模糊使用 RenderEffect 模糊使用 Vukan 模糊使用 GLSL 模糊RS、Vukan、RenderEffect、GLSL 效率对比 Android Bitmap 使用Vukan、RenderEffect、GLSL实现模糊 本文首发地址 https://blog.csdn.net/CSqingchen/articl…

【PixPin】媲美QQ/Snipaste截图贴图OCR工具

PixPin PixPin是一款截图工具&#xff0c;它集成了截图、长截图、贴图、标注、OCR识别等众多功能&#xff0c;软件体积小巧&#xff0c;使用简单&#xff0c;是一款非常棒的截图工具。之前使用过Snipaste工具的小伙伴用起来应该是得心应手。 从左往右的功能依次是&#xff1a;…

Python编程题集(第三部容器操作 )

Demo61 指定等级 题目描述 读入学生成绩&#xff0c;获取最高分best&#xff0c;然后根据下面的规则赋等级值&#xff1a; &#xff08;1&#xff09;如果分数≥best-10&#xff0c;等级为A &#xff08;1&#xff09;如果分数≥best-20&#xff0c;等级为B &#xff08;1…