设计模式(二)-创建者模式(4)-原型模式

一、为何需要原型模式(Prototype Pattern)?

在软件设计中,我们会遇到到这样的情况:对原对象进行拷贝一个新的副本。想要实现这样的逻辑,有一种笨方法就是对原对象里的所有变量进行逐一赋值。但是这样的做法会导致代码臃肿,逻辑复杂,还可能会有遗漏某些变量没有被赋值的问题。

为了解决这个在代码中对每一个变量进行赋值的问题,同时又保证能够实现拷贝的功能,于是原型模式就出来了。

定义: 用一个已经创建的实例作为原型,来创建一个与原型对象相同的新对象

【角色】

  • 抽象原型类: 规定具体原型对象必须实现的clone()方法
  • 实现原型类: 实现抽象原型类的clone()方法。

原型模式分为浅克隆和深克隆

浅克隆(浅拷贝):创建一个新的对象,新对象的属性和原型对象的完全相同。对对象里的引用类型成员,仍指向原来所指向的内存地址。(这里属性指的是:值类型。同下)

深克隆(深拷贝):创建一个新对象,对对象里的属性和引用类型成员都会被克隆,引用类型成员不再指向原来的地址。

应用条件:

  • 对象里有非常多成员,尤其该对象的引用类型成员里还有其他的引用类型对象。

二、例子

需求:

假设有一盏灯A,然后另一盏灯B根据A灯的样子造出来的。可以说这两盏灯外观一摸一样。在黑暗中,要想实现一个能有照明系统的用途,这两盏灯还需要连接一块电池才能发光照明。

假定电池为引用类型成员,A灯和B灯属于值类型成员。

A灯+电池 = 原对象。

B灯 +电池 = 新对象。

现有两种情况可以选择:

1)情况(浅拷贝):为了节约电池的成本,把这两盏灯都连接在同一块电池。

2)情况(深拷贝):为了这两盏灯要完全分开互不影响,一盏灯连接一块电池,所以需要两块容量一致的电池。

Serializable: 序列化,只对深拷贝有作用。系统遍历原对象里所有引用类型成员进行逐一克隆,无需代码来处理具体的过程。被标记为 Serializable 的类才能够使用深拷贝机制,否则程序运行会崩溃。

c 复制代码
   //引用类型:电池类
    [Serializable]
    public class Battery
    {
        public int Capacity;
    }

    //灯的原型抽象类
    [Serializable]
    public abstract class LampPrototype
    {
        public abstract int Use();

        public abstract LampPrototype ShallowClone();//浅拷贝
        public abstract LampPrototype DeepClone();//深拷贝
    }

    //灯的实现类
    [Serializable]
    public class Lamp : LampPrototype
    {
        //引用类型,电池
        public Battery battery = new Battery();

        public override int Use(){
            return battery.Capacity;
        }

        //浅拷贝
        public override LampPrototype ShallowClone(){
            return (Lamp)this.MemberwiseClone();
        }
        //深拷贝
        public override LampPrototype DeepClone(){
            MemoryStream stream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, this);
            stream.Position = 0;
            return (Lamp)formatter.Deserialize(stream);
        }

    }

    class Program
    {
        //设置原对象的数据
        public static void setOriginal(LampPrototype lampPro) {
            var lamp = lampPro as Lamp;
            lamp.battery.Capacity = 100;
        }

        //设置新对象的数据
        public static void setNew(LampPrototype lampPro) {
            var lamp = lampPro as Lamp;
            lamp.battery.Capacity = 80;
            Console.WriteLine("New Capacity::80");
        }

        static void Main(string[] args)
        {
            //原对象
            LampPrototype Originallamp = new Lamp();
            setOriginal(Originallamp);

            //浅拷贝
            Console.WriteLine();
            Console.WriteLine("After ShallowCopy,Original:");
            LampPrototype shallowAudio = Originallamp.ShallowClone();
            setNew(shallowAudio);//新对象使用电池,原对象的电池容量受到影响
            Console.WriteLine("Original Capacity:" + Originallamp.Use());

            //恢复原对象数据
            setOriginal(Originallamp);

            //深拷贝
            Console.WriteLine();
            Console.WriteLine("After DeepCopy,Original:");
            LampPrototype deepAudio = Originallamp.DeepClone();
            setNew(deepAudio);//新对象使用电池,原对象的电池容量没有受到影响
            Console.WriteLine("Original Capacity:" + Originallamp.Use());

            Console.ReadLine();
        }
    }
相关推荐
weixin_460443562 小时前
2026年新修订《网络安全法》实施:Java私有化考试系统如何做好SBOM、漏洞治理与安全升级?
java·开发语言·在线考试系统·企业培训考试系统·宏远培训考试系统·国企培训考试系统·国产化培训系统
海鸥-w5 小时前
什么是AOP
java
Cc.Y5 小时前
Java零基础入门:可变字符串与包装类:StringBuilder、StringBuffer 与 通讯录管理系统实战
java·开发语言·python
paopaokaka_luck5 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787055 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
阿森_dev5 小时前
2027 届毕设选题怎么选:30 个 Java 方向选题的实现难度排序
java·开发语言·spring boot·毕业设计·课程设计
Sarvartha5 小时前
Lambda 表达式和函数式接口
java·开发语言
计算机毕业设计杰瑞6 小时前
【计算机毕设精品】基于SpringBoot的一体化教务与学业质量管理系统的设计与实现,附源码_增删改查_java项目_可完美运行部署_网站项目
java·spring boot·课程设计
colinu6 小时前
C++ 异步任务与线程生命周期:`std::async`、`future`、`thread`、`jthread` 与协作式退出
java·jvm·c++
蜗牛互联网6 小时前
Python + JSON Schema 实现工单结构化输出与本地复核
java·人工智能·后端