java设计模式之原型模式《Ctrl+V救赎》

"南方的小镇,阴雨的冬天,没有北方冷~~~~" 微信的语音来电提示在工位上响个不停,刚上完WC回来的小白盯着手机微信上列出的996个未接语音,心头一紧,没来得及擦干净的手赶紧点了回拨。刚打通,屏幕另一端,老板兴奋的声音就飙了过来:"小白啊!我刚在元宇宙论坛被投屏了!未来一定是元宇宙的,这样,你简单搞下,咱们必须上线个史诗级H5——'生成你的五彩斑斓黑分身'!",文档我先发你,你看下好吧。
“叮咚”,文件发过来了,小白点开了文档,他的血压随着内容的进度而升高。
史诗级需求文档(节选)​

      核心科技

  • 每个用户生成 ​1万+ 虚拟形象(要像海底捞甩面一样丝滑!)

  • 支持实时换装,发色要能展示 ​**#000000到#FFFFFF的65536种渐变黑**
    创新亮点

  • 分身瞳孔必须 ​每分钟自动生成克苏鲁风格几何纹​(参考《三体》二向箔特效)

  • 服装材质需 ​同步现实温度​(比如穿羽绒服分身会出汗,但老板没说怎么检测室温😶)
    ** Deadline ​**:

  • 3小时后 我要在董事会上演示(附带20页PPT脑图,文件名:《这个需求很简单.txt》)

小白的脑内弹幕

  1. "五彩斑斓的黑?您是要我把RGB通道拧成麻花吗?!"
  2. "1万分身?这怕不是要我写个分布式克隆流水线..."
  3. "三小时?我连Photoshop图层都没解完!"

(手指悬在「立即发送」辞职邮件的按钮上,突然想起花呗待还金额——按回车的手,微微颤抖)

叹了口气,小白只能开始干活了。代码如下

 /**
     * 虚拟形象类(存在初始化成本)
     */
    public class Avatar {
        private String hairColor;
        private List<String> textures = new ArrayList<>(); // 贴图资源
        
        // 构造方法包含重量级初始化(模拟实际业务场景)
        public Avatar() {
            // 模拟耗时操作(实际可能是加载3D模型、读取配置文件等)
            System.out.println("⚠️ 正在初始化基础模型(耗时2秒)...");
            try {
                Thread.sleep(2000); // 模拟模型加载耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // 加载基础贴图(占用内存)
            loadBaseTextures(); 
        }

        private void loadBaseTextures() {
            textures.add("皮肤贴图"); 
            textures.add("服装基础材质");
            // 实际项目可能加载10MB+的纹理资源
        }

        public void setHairColor(String color) {
            this.hairColor = color;
        }
    }

开始执行代码

    public static void main(String[] args) {
        System.out.println("🚨 老板需求:生成10000个差异化形象");
        
        // ==============================================
        // 问题代码区(传统实现方式)
        // ==============================================
        List<Avatar> avatars = new ArrayList<>();
        
        for (int i = 0; i < 10000; i++) {
            // 痛点1:每次new都重复执行完整初始化
            Avatar avatar = new Avatar(); 
            
            // 痛点2:差异化配置成本被重复初始化淹没
            avatar.setHairColor("#" + String.format("%06d", i)); // 微调发色
            
            avatars.add(avatar);
            
            // 模拟内存问题(实际JVM会提前崩溃)
            if (i % 100 == 0) {
                System.out.printf("🌀 已创建 %d 个实例 | 预计内存占用:%.1fMB\n", 
                    i, (i * 0.5)); // 假设每个实例占0.5MB
            }
        }

        System.out.println("❌ 实际执行结果:");
        System.out.println("1. 总耗时:" + (10000 * 2) + "秒 ≈ 5.5小时(老板已开除你10次)");
        System.out.println("2. 内存占用:" + (10000 * 0.5) + "MB = 5GB(手机用户直接闪退)");
        System.out.println("3. 显卡温度:89℃(成功烤熟早餐鸡蛋)");
        
    }

执行完后的电脑已经冒烟了,随之而来的是小白瘫倒在椅子上。。。想到花呗没还,立马弹跳了起来,进行了问题分析。

1.​重量级初始化问题

public Avatar() {
    Thread.sleep(2000); // 每个对象都重复加载基础资源
    loadBaseTextures(); // 每次创建都占用新内存
}
  • 问题:相同的基础模型被重复加载10000次
  • 后果:初始化时间呈指数级增长

 2.内存消耗叠加
 

List<Avatar> avatars = new ArrayList<>(); // 存储大量重复基础数据的对象
  • 问题:每个对象都包含相同的贴图资源副本
  • 后果:内存占用从5MB(共享) → 5GB(独立存储)

3.差异化成本被淹没 

avatar.setHairColor("#"+i); // 有效操作只有0.1% 
  • 问题:99.9%的资源消耗在重复配置上
  • 对比:理想情况应只差异化修改发色,复用其他配置

小白盯着满屏的OutOfMemoryError报错,第N次尝试减少内存占用的努力再次失败。正当他准备把显示器变成投掷武器时,余光瞥见书架上的《设计模式:程序员防猝死指南》,封面积灰的章节标题突然闪烁——原型模式:对象克隆的艺术

"等等...克隆?" 小白混沌的大脑突然划过闪电,"如果每个分身不用从头new,而是像细胞分裂那样复制呢?"
(机械键盘突然发出圣洁的白光,仿佛在说:"少年,你悟了!")

什么是原型者模式?
通过克隆现有对象来创建新对象,避免重复执行昂贵的初始化操作,就像生物体的细胞分裂一样高效

修改后的代码如下

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

/**
 * 原型模式重构版 - 元宇宙分身生成系统
 * 核心优化点:通过克隆避免重复初始化,实现高效对象创建
 */
public class PrototypePatternDemo {

    /**
     * 虚拟形象类(实现Cloneable接口)
     */
    static class Avatar implements Cloneable {
        private String id;
        private String hairColor;
        private List<String> textures = new ArrayList<>(); // 贴图资源
        private String specialEffect;

        /**
         * 构造方法(仅用于创建初始原型)
         * @param baseColor 基础发色
         */
        public Avatar(String baseColor) {
            System.out.println("⚙️ 正在创建珍贵的基础原型...");
            this.hairColor = baseColor;
            initializeHeavyResources(); // 模拟耗时/耗资源的初始化
        }

        /**
         * 模拟重量级初始化操作
         * (实际可能是加载3D模型、读取配置文件等)
         */
        private void initializeHeavyResources() {
            // 模拟耗时操作
            try {
                Thread.sleep(2000); // 2秒初始化时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // 加载基础贴图(模拟内存占用)
            textures.add("高精度皮肤贴图");
            textures.add("4K服装材质");
        }

        /**
         * 关键克隆方法(实现对象复制)
         */
        @Override
        public Avatar clone() {
            try {
                Avatar cloned = (Avatar) super.clone();
                // 深拷贝处理(重要!)
                cloned.textures = new ArrayList<>(this.textures); // 创建新集合
                cloned.id = generateNewId(); // 生成新ID
                return cloned;
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException("克隆失败: " + e.getMessage());
            }
        }

        // 生成唯一ID(模拟业务需求)
        private String generateNewId() {
            return "AVATAR-" + System.nanoTime();
        }

        // 以下为配置方法 ---------------
        public void setHairColor(String color) {
            this.hairColor = color + "_GLOSS"; // 老板要求的"五彩斑斓的黑"
        }

        public void addEffect(String effect) {
            this.specialEffect = effect;
        }

        public void printInfo() {
            System.out.printf("| %-12s | %-15s | %-20s | %-15s |\n",
                    id, hairColor, textures, specialEffect);
        }
    }

    /**
     * 原型管理器(最佳实践封装)
     */
    static class PrototypeManager {
        private static final Avatar BASE_PROTOTYPE = new Avatar("#000000");

        // 获取基础克隆体
        public static Avatar getBaseClone() {
            return BASE_PROTOTYPE.clone();
        }
    }

    /**
     * 演示对比(传统 vs 原型)
     */
    public static void main(String[] args) {
        System.out.println("\n=== 传统方式 ===");
        traditionalApproach();

        System.out.println("\n=== 原型模式 ===");
        prototypeApproach();
    }

    /**
     * 传统方式 - 直接new对象(问题明显)
     */
    private static void traditionalApproach() {
        long start = System.currentTimeMillis();
        List<Avatar> avatars = new ArrayList<>();

        for (int i = 0; i < 3; i++) { // 示例只生成3个,实际需上万
            Avatar avatar = new Avatar("#000000");
            avatar.setHairColor("#00000" + i);
            avatar.addEffect("传统特效" + i);
            avatars.add(avatar);
        }

        System.out.println("ID            | 发色            | 贴图资源          | 特效          ");
        avatars.forEach(Avatar::printInfo);
        System.out.println("🕒 耗时: " + (System.currentTimeMillis()-start) + "ms");
        System.out.println("💾 内存估算: " + (3 * 2.5) + "MB (实际会OOM)"); // 每个按2.5MB估算
    }

    /**
     * 原型模式 - 克隆对象(性能优化)
     */
    private static void prototypeApproach() {
        long start = System.currentTimeMillis();
        List<Avatar> avatars = new ArrayList<>();
        
        // 预先创建基础原型(仅一次初始化)
        Avatar prototype = PrototypeManager.getBaseClone();

        for (int i = 0; i < 3; i++) {
            Avatar clone = prototype.clone();
            clone.setHairColor("#00000" + i);
            clone.addEffect("炫酷特效" + i);
            avatars.add(clone);
        }

        System.out.println("ID            | 发色            | 贴图资源          | 特效          ");
        avatars.forEach(Avatar::printInfo);
        System.out.println("🕒 耗时: " + (System.currentTimeMillis()-start) + "ms");
        System.out.println("💾 内存优化: 基础512MB + 差异" + (3 * 0.1) + "MB");
    }
}

 关键代码注释

// 深拷贝核心代码(防止数据污染)
@Override
public Avatar clone() {
    Avatar cloned = (Avatar) super.clone(); // 浅拷贝基础字段
    cloned.textures = new ArrayList<>(this.textures); // 创建新集合(重要!)
    // 如果不做深拷贝,所有克隆体会共享同一个textures列表
    // 修改任意克隆体的textures会影响所有对象!
}

// 原型管理器(防止原型被修改)
static class PrototypeManager {
    private static final Avatar BASE_PROTOTYPE = new Avatar("#000000");
    // 私有化构造防止外部new实例
    public static Avatar getBaseClone() {
        return BASE_PROTOTYPE.clone(); // 每次返回克隆体,保护原型
    }
}

// 传统方式的致命问题
Avatar avatar = new Avatar(); // 每次都要经历2秒初始化
// 当需要1万个对象时:2秒 * 10000 = 5.5小时!

 核心优势总结

  1. 时间效率

    • 传统:10000对象 × 2秒 = ​5.5小时
    • 原型:2秒(初始) + 10000×0.001秒 = ​12秒
  2. 内存优化

    • 传统:10000 × 2.5MB = ​25GB​(直接OOM)
    • 原型:512MB(基础) + 10000×0.1MB = ​1.5GB
  3. 可维护性

    • 新增"赛博朋克"风格只需添加新原型
    • 修改基础配置只需改原型,所有克隆体自动继承

适用性评估矩阵

办公室的电子钟跳动着血红色的数字,小白的指尖在键盘上擦出火星。当最后一个clone()方法通过测试时,他猛地抓起手机——

"老板!搞定了!" 通话界面的计时器显示:​2:59:59
屏幕那端传来鼠标点击声,随后是漫长的沉默。突然——"这...这特效!" 老板的声音因激动而破音,"五彩斑斓的黑居然会随呼吸渐变!等等,这个分身怎么在给我倒咖啡?!"
小白瞟了一眼代码注释:

// 彩蛋功能:老板分身自动打工模块  
// 如果用户ID是CEO,克隆体将自动工作23小时/天  
clone.setAutoWorkingMode(true); // 用魔法打败魔法  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值