初学 Java 设计模式(五):实战原型模式 「英雄联盟齐天大圣-真假猴王」

提交者
• 阅读 1429

一、原型模式介绍

1. 解决的问题

主要解决的问题就是创建重复对象,这部分对象内容本身比较复杂,生成过程可能从库中或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间。

2. 定义

原型模式是一种创建型模式,能够复制已有对象,而又无需使代码依赖它们所属的类。

3.应用场景

  • 对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算。
  • 需要复制一些对象,同时又希望代码独立于这些对象所属的具体类。

    这种场景通常出现在代码需要处理第三方代码通过接口传递过来的对象时。即使不考虑代码耦合的情况,我们的代码也不能依赖这些对象所属的具体类,因为不知道其具体信息。

    原型模式为我们的代码提供了一个通用接口,我们可以通过这一接口与所有实现了克隆的对象进行交互,使得我们的代码与所克隆的对象具体类独立开。

  • 子类的区别仅在于其对象的初始化方式,可以使用原型模式来减少子类的梳理。

    创建这些子类的目的可能只是为了创建特定类型的对象。

原型模式已与 Java 融为一体,可以随手拿来使用:

  • Objectclone() 方法。
  • Cloneable 接口的实现类,至少可以看到一千多个实现。

4. 浅拷贝与深拷贝

在 Java 中,数据类型分为值类型和引用类型,值类型包括 int、double、byte、boolean、char 等基本数据类型,引用类型包括类、接口、数组等复杂类型。

浅拷贝与深拷贝的主要区别在于是否支持引用类型的成员变量的复制

  1. 浅拷贝

    在浅拷贝中,若原型对象的成员变量是值类型,将复制给克隆对象;若原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,即原型对象和克隆对象的成员变量指向相同的内存地址。

    简单来说,在浅拷贝中,对象被复制时只复制其本身和其包含的值类型的成员变量,而引用类型的成员对象并没有复制

    在 Java 中,通过覆盖 Object 类的 clone() 方法即可实现浅拷贝。

  2. 深拷贝

    在深拷贝中,无论原型变量的成员变量是值类型还是引用类型,都将复制给克隆对象。

    简单来说,在深拷贝中,除了对象本身被复制外,对象所包含的所有成员变量也将复制

    在 Java 中,实现深克隆,可以通过复制引用对象或序列化读取二进制流等方式实现,需要注意的是,能够序列化的对象必须实现 Serializable 接口

    实现 Serializable 读取二进制流示例:

    protected Object deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException {
        //将对象写入流中
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);
    
        //将对象从流中取出
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (ois.readObject());
    }

二、原型模式优缺点

1. 优点

  • 可以克隆对象,而无需与其所属的具体类相耦合。
  • 可以克隆生成原型,避免反复运行初始化代码。
  • 可以更方便的生成复杂对象。
  • 可以使用继承以外的方式来处理复杂对象的不同配置。

2. 缺点

  • 克隆包含循环引用的复杂对象可能会非常麻烦。

三、原型模式应用实例:英雄联盟齐天大圣-真假猴王

1. 实例场景

英雄联盟中有一个英雄叫齐天大圣 · 孙悟空,这个名字想必我们都很熟悉,而在英雄联盟中,它的人物背景是这样的:

悟空是一个瓦斯塔亚族的机灵鬼,用自己的力量、灵敏和机智迷惑对手并抢得先机。机缘巧合让他结识了一位剑客并与之成为一生的挚友,这位剑客被人称作易大师。后来,悟空就成为了古老武术门派“无极”的最后一位弟子。如今,附魔长棍傍身的悟空,目标是让艾欧尼亚免遭崩溃的命运。

“我的游记才刚刚开始……”——齐天大圣 · 孙悟空

齐天大圣 · 孙悟空有一个技能叫做真假猴王:

孙悟空进入持续1秒的隐形状态并朝着一个方向突进300距离(不能越过墙体),同时留下一个会攻击附近敌人的分身。

孙悟空的分身会模仿孙悟空的普通攻击和终极技能,但是仅造成35/40/45/50/55%伤害。

今天我们就使用创造孙悟空的分身作为模拟场景。

2. 原型模式实现

2.1 工程结构
builder-pattern
└─ src
    ├─ main
    │    └─ java
    │    └─ org.design.pattern.prototype
    │       ├─ model
    │          ├─ Hero.java
    │          └─ WuKong.java
    └─ test
        └─ java
            └─ org.design.pattern.prototype.test
                  └─ WuKongTest.java
2.2 代码实现

英雄基类

/**
 * 英雄基类
 */
@Getter
@Setter
@NoArgsConstructor
public abstract class Hero {
    /**
     * 生命值
     */
    protected int healthPoint;

    /**
     * 法力值
     */
    protected int magicPoint;

    /**
     * 物理伤害
     */
    protected int attackDamage;

    /**
     * 法术伤害
     */
    protected int abilityPower;

    public Hero(Hero hero) {
        if (hero != null) {
            this.healthPoint = hero.healthPoint;
            this.magicPoint = hero.magicPoint;
            this.attackDamage = hero.attackDamage;
            this.abilityPower = hero.abilityPower;
        }
    }

    public abstract Hero clone();

    /**
     * 攻击
     */
    public void attach() {
        System.out.printf("This hero’s attack caused %d damage", this.attackDamage);
        System.out.println();
    }
}

齐天大圣 · 孙悟空

/**
 * 齐天大圣孙悟空
 */
@Getter
@Setter
@NoArgsConstructor
public class WuKong extends Hero {
    /**
     * 物理伤害比例
     */
    protected float attackDamageProportion = 1;

    public WuKong(WuKong wuKong) {
        super(wuKong);
        if (wuKong != null) {
            this.healthPoint = wuKong.healthPoint;
            this.magicPoint = wuKong.magicPoint;
            this.attackDamage =  (int) (wuKong.attackDamage * wuKong.attackDamageProportion);
            this.abilityPower = wuKong.abilityPower;
        }
    }

    @Override
    public WuKong clone() {
        return new WuKong(this);
    }

    /**
     * 攻击
     */
    public void attach() {
        super.attach();
    }
}
2.3 测试验证
2.3.1 测试验证类
public class WuKongTest {
    @Test
    public void testWuKong() {
        //齐天大圣孙悟空
        WuKong wuKong = new WuKong();
        wuKong.setHealthPoint(540);
        wuKong.setMagicPoint(300);
        wuKong.setAttackDamage(68);
        wuKong.setAbilityPower(54);
        //复制孙悟空分身
        wuKong.setAttackDamageProportion(0.3f);
        WuKong fakeKong = wuKong.clone();
        System.out.println("孙悟空攻击:");
        wuKong.attach();
        System.out.println("孙悟空分身攻击:");
        fakeKong.attach();
    }
}
2.3.2 测试结果
孙悟空攻击:
This hero’s attack caused 68 damage
孙悟空分身攻击:
This hero’s attack caused 20 damage

四、原型模型结构

初学 Java 设计模式(五):实战原型模式 「英雄联盟齐天大圣-真假猴王」

  1. 原型 (Prototype) 接口将对克隆方法进行声明。在绝大多数情况下,其中只会有一个名为 clone克隆的方法。
  2. 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归依赖等等。
  3. 客户端 (Client) 可以复制实现了原型接口的任何对象。

设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。

源码地址:https://github.com/yiyufxst/d...

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei...
深入设计模式:https://refactoringguru.cn/de...

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
java中的23种设计模式
java中一共23种设计模式!按照目的来分,设计模式可以分为创建型模式、结构型模式和行为型模式。创建型模式用来处理对象的创建过程;结构型模式用来处理类或者对象的组合;行为型模式用来对类或对象怎样交互和怎样分配职责进行描述。创建型模式用来处理对象的创建过程,主要包含以下5种设计模式:工厂方法模
灯灯灯灯 灯灯灯灯
4年前
「超全超细」Java设计模式图文详解!!!
java设计模式—原型模式Java原型模式1、概述  啥是原型模式?  原型模式属于设计模式中的创建型中的一员,  原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象!  说大白话就是自己复制自己,通过原生对象复制出一个新的对象,这两个对象结构相同且相似;  需要注意的是,原型对象自己不仅是个对象还是个工厂!并且通过克隆方式创
Stella981 Stella981
3年前
JavaScript面向对象编程的15种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”。在JavaScript中并没有类这种概念,面向对象编程不是基于类,而是基于原型去面向对象编程,JS中的函数属于一等对象,而基于JS中闭包与弱类型等特性,在实现一些设计模式的方式上与众不同。ps:本文之讲述面向对象编程的设计模式策略,JavaScript原型的基础请参考阮一峰面向
Wesley13 Wesley13
3年前
Java中23种设计模式详解
Java中23种设计模式1\.设计模式31.1创建型模式41.1.1工厂方法41.1.2抽象工厂61.1.3建造者模式101.1.4单态模式131.1.5原型模式151.2结构型模式171.2.1适配器模式171.2.2桥接模式191.2.3组合
Wesley13 Wesley13
3年前
Java设计模式之原型模式
原型模式简介原型模式实际上不算一种设计模式,应该说是一种技巧吧。当我们需要创建与已有对象一样的对象时,我们通常可以有两种容易想到的方法,一种是将已有对象指向另外一个重新创建的对象,如//将old赋给newObjectnewObjectoldObject;这种做法是相当于newObject还是指向
Wesley13 Wesley13
3年前
Java原型模式
原型模式  原型模式也称克隆模式。原型模式jianmingzhiyi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new。new创造出来的对象采用的是默认值。克隆出来的对象与原型对象相同,同时不会影响原型对象,然后在修改克隆出来的对象。实现  继承Cloneable接口,重写cl
Wesley13 Wesley13
3年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Stella981 Stella981
3年前
Javascript继承5:如虎添翼
/寄生式继承其实就是对原型继承的第二次封装,在封装过程中对继承的对象进行了扩展。也存在原型继承的缺点!!这种思想的作用也是为了寄生组合式继承模式的实现。///声明基对象varbook{name:'jsbook',al
Wesley13 Wesley13
3年前
23种设计模式(面向对象语言)
一、设计模式的分类总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。所有的创建型模式都有两个主要功能:  1.将系统所使用的具体类的信息封装起来  2.隐藏
设计模式-单例模式概述 | 京东云技术团队
我们常把23种经典的设计模式分为三类:创建型、结构型、行为型,其中创建型设计模式主要解决“对象的创建”问题,将创建和使用代码解耦,结构型设计模式主要解决“类或对象的组合或组装”问题,将不同功能代码解耦,行为型设计模式主要解决“类或对象之间的交互”问题,将不