讲故事

最近重温了一下星爷的《唐伯虎点秋香》,依然让我捧腹不已,幻想着要是我也能有一名秋香如此的侍女,夫复何求呀,带着这个美好的幻想沉沉睡去...

突然想到,我是一名程序猿呀,想要什么对象不是易如反掌吗,New一个呗,不光是秋香,春、夏、冬都要,身材要超A的,百度好三围(82, 54, 86),开干...

Coding

Beauty类,包含美人的属性

    public class Beauty
{
public Beauty(int bust, int theWaist, int hipline)
{
Bust = bust;
TheWaist = theWaist;
Hipline = hipline;
//模拟产生一名美人的时长
Thread.Sleep(3000);
} /// <summary>
/// 名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 才艺
/// </summary>
public string Talent { get; set; } /// <summary>
/// 胸围
/// </summary>
public int Bust { get; set; } /// <summary>
/// 腰围
/// </summary>
public int TheWaist { get; set; } /// <summary>
/// 臀围
/// </summary>
public int Hipline { get; set; } /// <summary>
/// 起名
/// </summary>
/// <param name="name"></param>
public void SetName(string name)
{
Name = name;
} /// <summary>
/// 设置才艺
/// </summary>
/// <param name="talent"></param>
public void SetTalent(string talent)
{
Talent = talent;
}
}

客户端生产美女

    internal class Program
{
private static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
var beauty1 = new Beauty(82, 54, 86);
sw.Stop();
Console.WriteLine($"生产第一名美人耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty1.SetName("秋香");
beauty1.SetTalent("弹琴"); sw.Restart();
var beauty2 = new Beauty(82, 54, 86);
sw.Stop();
Console.WriteLine($"生产第二名美人耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty2.SetName("春香");
beauty2.SetTalent("画画"); sw.Restart();
var beauty3 = new Beauty(82, 54, 86);
sw.Stop();
Console.WriteLine($"生产第三名美人耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty3.SetName("夏香");
beauty3.SetTalent("舞蹈"); Show(beauty1);
Show(beauty2);
Show(beauty3); Console.WriteLine("\nHappy Ending~");
Console.ReadLine();
} public static void Show(Beauty beauty)
{
Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],我的才艺是 {beauty.Talent}");
}
}

结果展示:

生产第一名美女耗时:3008/ms

生产第二名美女耗时:3001/ms

生产第三名美女耗时:3000/ms

我是 秋香,身材[82-54-86],我的才艺是 弹琴
我是 春香,身材[82-54-86],我的才艺是 画画
我是 夏香,身材[82-54-86],我的才艺是 舞蹈

我的美人产生了,但就是每次都是通过New创建,设置的标准身材(82-54-86)不变但每次都要设置,而且每次耗时都很长,要是再生产更多,岂不劳累又耗时...

正在苦恼之时,突然灵感乍现,可以使用原型模式(Prototype)解决呀

Code Upgrade

原型模式 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

其实就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节

.NETSystem命名空间中提供了ICloneable接口,其中只有一个方法Clone(),实现这个接口就可以完成原型模式了。

开始改造:

Beauty实现ICloneable接口

    public class Beauty : ICloneable
{
public Beauty(int bust, int theWaist, int hipline)
{
Bust = bust;
TheWaist = theWaist;
Hipline = hipline;
//模拟产生一名美人的时长
Thread.Sleep(3000);
} /// <summary>
/// 名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 才艺
/// </summary>
public string Talent { get; set; } /// <summary>
/// 胸围
/// </summary>
public int Bust { get; set; } /// <summary>
/// 腰围
/// </summary>
public int TheWaist { get; set; } /// <summary>
/// 臀围
/// </summary>
public int Hipline { get; set; } /// <summary>
/// 起名
/// </summary>
/// <param name="name"></param>
public void SetName(string name)
{
Name = name;
} /// <summary>
/// 设置才艺
/// </summary>
/// <param name="talent"></param>
public void SetTalent(string talent)
{
Talent = talent;
} public object Clone()
{
return this.MemberwiseClone();
}
}

客户端生产时,除第一个调用new()外,其他使用Clone()方法创建

    internal class Program
{
private static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
var beauty1 = new Beauty(82, 54, 86);
sw.Stop();
Console.WriteLine($"生产第一名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty1.SetName("秋香");
beauty1.SetTalent("弹琴"); sw.Restart();
var beauty2 = (Beauty)beauty1.Clone();
sw.Stop();
Console.WriteLine($"生产第二名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty2.SetName("春香");
beauty2.SetTalent("画画"); sw.Restart();
var beauty3 = (Beauty)beauty1.Clone();
sw.Stop();
Console.WriteLine($"生产第三名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty3.SetName("夏香");
beauty3.SetTalent("舞蹈"); Show(beauty1);
Show(beauty2);
Show(beauty3); Console.WriteLine("\nHappy Ending~");
Console.ReadLine();
} public static void Show(Beauty beauty)
{
Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],我的才艺是 {beauty.Talent}");
}
}

结果展示:

生产第一名美女耗时:3009/ms

生产第二名美女耗时:0/ms

生产第三名美女耗时:0/ms

我是 秋香,身材[82-54-86],我的才艺是 弹琴
我是 春香,身材[82-54-86],我的才艺是 画画
我是 夏香,身材[82-54-86],我的才艺是 舞蹈

我们可以看到,除了第一个创建耗时以外,其他Clone出来的对象基本不耗时,而且不用重复设置固定属性(三围)(一般在初始化的信息不发生变化的情况下,克隆是最好的方法,既隐藏了对象创建的细节,又对性能大大的提高),我心甚欢...

于是想着对美人的才艺进行升级

Talent类,对才艺描述,增加才艺段位

    public class Talent
{
/// <summary>
/// 才艺名
/// </summary>
public string Name { get; set; } /// <summary>
/// 段位
/// </summary>
public int Level { get; set; }

Beauty类改造

    public class Beauty : ICloneable
{
public Beauty(int bust, int theWaist, int hipline)
{
Bust = bust;
TheWaist = theWaist;
Hipline = hipline;
Talent = new Talent();
//模拟产生一名美人的时长
Thread.Sleep(3000);
} /// <summary>
/// 名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 才艺
/// </summary>
public Talent Talent { get; set; } /// <summary>
/// 胸围
/// </summary>
public int Bust { get; set; } /// <summary>
/// 腰围
/// </summary>
public int TheWaist { get; set; } /// <summary>
/// 臀围
/// </summary>
public int Hipline { get; set; } /// <summary>
/// 起名
/// </summary>
/// <param name="name"></param>
public void SetName(string name)
{
Name = name;
} /// <summary>
/// 设置才艺
/// </summary>
/// <param name="talent"></param>
public void SetTalent(string name, int level)
{
Talent.Name = name;
Talent.Level = level;
} public object Clone()
{
return this.MemberwiseClone();
}
}

客户端设置才艺段位:

    internal class Program
{
private static void Main(string[] args)
{
var sw = new Stopwatch();
sw.Start();
var beauty1 = new Beauty(82, 54, 86);
sw.Stop();
Console.WriteLine($"生产第一名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty1.SetName("秋香");
beauty1.SetTalent("弹琴", 10); sw.Restart();
var beauty2 = (Beauty)beauty1.Clone();
sw.Stop();
Console.WriteLine($"生产第二名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty2.SetName("春香");
beauty2.SetTalent("画画", 9); sw.Restart();
var beauty3 = (Beauty)beauty1.Clone();
sw.Stop();
Console.WriteLine($"生产第三名美女耗时:{sw.ElapsedMilliseconds}/ms\n");
beauty3.SetName("夏香");
beauty3.SetTalent("舞蹈", 8); Show(beauty1);
Show(beauty2);
Show(beauty3); Console.WriteLine("\nHappy Ending~");
Console.ReadLine();
} public static void Show(Beauty beauty)
{
Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],我的才艺是 {beauty.Talent.Name} 段位 {beauty.Talent.Level}");
}
}

结果展示:

生产第一名美女耗时:3022/ms

生产第二名美女耗时:0/ms

生产第三名美女耗时:0/ms

我是 秋香,身材[82-54-86],我的才艺是 舞蹈 段位 8
我是 春香,身材[82-54-86],我的才艺是 舞蹈 段位 8
我是 夏香,身材[82-54-86],我的才艺是 舞蹈 段位 8

看到结果我懵了,什么情况,我明明把才艺设置的不一样,怎么三个最后都是 [舞蹈 段位 8] ???

良久平息之后,才明白导致上面结果的原因,也将是本文的重点:浅复制与深复制

浅复制与深复制

浅复制:复制得到的对象的所有变量都包含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

MemberwiseClone()方法就是浅复制,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用对象,因此原始对象及其复本引用同一对象。

这就是上面,三个美人的才艺最后都变成一样的原因,才艺Talent是一个引用类型,复制三份的是Talent的引用,都指向同一个Talent对象

深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

解决上面的问题,我们就要用深复制,要对Talent复制一个新对象,而不是一个引用。('美人'引用了'才艺',假如'才艺'里还有 引用,很多层,就涉及到深复制要深入多少层的问题,需要事先考虑好,而且要当心出现循环引用的问题,本次案例我们就深入到第一层就可以了)

上代码:

Talent类,实现ICloneable接口

    public class Talent : ICloneable
{
/// <summary>
/// 才艺名
/// </summary>
public string Name { get; set; } /// <summary>
/// 段位
/// </summary>
public int Level { get; set; } public object Clone()
{
return this.MemberwiseClone();
}
}

Beauty类,增加私有构造函数,以便克隆“才艺”的克隆数据

    public class Beauty : ICloneable
{
public Beauty(int bust, int theWaist, int hipline)
{
Bust = bust;
TheWaist = theWaist;
Hipline = hipline;
Talent = new Talent();
//模拟产生一名美人的时长
Thread.Sleep(3000);
} private Beauty(Talent talent)
{
this.Talent = (Talent)talent.Clone();
} /// <summary>
/// 名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 才艺
/// </summary>
public Talent Talent { get; set; } /// <summary>
/// 胸围
/// </summary>
public int Bust { get; set; } /// <summary>
/// 腰围
/// </summary>
public int TheWaist { get; set; } /// <summary>
/// 臀围
/// </summary>
public int Hipline { get; set; } /// <summary>
/// 起名
/// </summary>
/// <param name="name"></param>
public void SetName(string name)
{
Name = name;
} /// <summary>
/// 设置才艺
/// </summary>
/// <param name="talent"></param>
public void SetTalent(string name, int level)
{
Talent.Name = name;
Talent.Level = level;
} public object Clone()
{
//调用私有构造方法,让“才艺”克隆完成,然后再给这个 “美人” 对象的相关字段赋值,
//最终返回一个深复制的 “美人” 对象
var beauty = new Beauty(Talent)
{
Bust = this.Bust,
TheWaist = this.TheWaist,
Hipline = this.Hipline
};
return beauty;
}
}

客户端通以前一样,展示结果:

生产第一名美女耗时:3008/ms

生产第二名美女耗时:0/ms

生产第三名美女耗时:0/ms

我是 秋香,身材[82-54-86],我的才艺是 弹琴 段位 10
我是 春香,身材[82-54-86],我的才艺是 画画 段位 9
我是 夏香,身材[82-54-86],我的才艺是 舞蹈 段位 8

看到结果,我心甚喜。突然屋内彩虹环绕,三位美人出现在我面前,婀娜妖娆,不对,怎么少了我的冬香,爬向键盘...

叮铃铃,叮铃铃...闹铃响了

握艹,我要续梦~

源码地址: https://gitee.com/sayook/DesignMode/tree/master/Prototype

设计模式-原型模式(Prototype)【重点:浅复制与深复制】的更多相关文章

  1. PHP设计模式 原型模式(Prototype)

    定义 和工厂模式类似,用来创建对象.但实现机制不同,原型模式是先创建一个对象,采用clone的方式进行新对象的创建. 场景 大对象的创建. 优点 1.可以在运行时刻增加和删除产品 2.可以改变值或结构 ...

  2. [工作中的设计模式]原型模式prototype

    一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...

  3. 设计模式——原型模式(Prototype)

    用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.——DP UML类图 模式说明 如果把在一张纸上手写一篇简历的过程看成是类的实例化过程,那么通过原型模式创建对象的过程就是拿着这张纸到复印 ...

  4. 设计模式-原型模式(Prototype)

    场景分析: 前面我们提到,交易对象Trade,还有继承他的债券交易BondTrade.期货交易FutureTrade. 现在有一个需求,需要提供方法将交易拆分成多笔小交易. 代码如下(如果没有clon ...

  5. 设计模式--原型模式Prototype(创建型)

    一.原型模式 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.原型模式实现的关键就是实现Clone函数,还需要实现深拷贝. 二.UML类图 三.例子 //父类 class Resume ...

  6. 谈谈设计模式~原型模式(Prototype)

    返回目录 原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例(clone),而不是新建(new)实例.被复制的实例就是我们所称的“原型”,这个原型是可定制的. 原型模式 ...

  7. C#设计模式——原型模式(Prototype Pattern)

    一.概述 在软件开发中,经常会碰上某些对象,其创建的过程比较复杂,而且随着需求的变化,其创建过程也会发生剧烈的变化,但他们的接口却能比较稳定.对这类对象的创建,我们应该遵循依赖倒置原则,即抽象不应该依 ...

  8. 设计模式——原型模式(Prototype Pattern)

    原型模式:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象. UML 图: 原型类: package com.cnblog.clarck; /** * 原型类 * * @author c ...

  9. 大话设计模式--原型模式 Prototype -- C++实现

    1. 原型模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象... 注意: 拷贝的时候是浅拷贝 还是 深拷贝, 来考虑是否需要重写拷贝构造函数. 关键在于: virtual Pro ...

随机推荐

  1. Django-rest-framework源码分析(三)

    APIView(二) 在APIView执行dispatch()方法的时候,它不仅做了分发函数的功能,还进行了一系列的校验. Django的全局校验一般写在中间件中,但是rest_framework于D ...

  2. Springcloud 整合Hystrix 断路器,支持Feign客户端调用

    1,在这篇博文中,已经大致说过了Springcloud服务保护框架 Hystrix在服务隔离,服务降级,以及服务熔断中的使用 https://www.cnblogs.com/pickKnow/p/11 ...

  3. Springcloud zuul 实现API 网关

    1,https://github.com/Netflix/zuul zuul 网关文档 2,什么是网关 网关就是客户端进行访问的时候,先经过网关服务器,再由网关服务器进行转发到真实的服务器.类似于Ng ...

  4. 基于Andriod的简易计算器

    这学期有安卓这门课,这里做了一个简易的计算器,实现了两位数加减乘除的基本功能,比较简单适合用来入门学习. 运行效果 预备知识 实现这个计算器之前要先了解实现计算器需要的基本组件 1.TextView ...

  5. Springboot使用自定义注解实现简单参数加密解密(注解+HandlerMethodArgumentResolver)

    前言 我黄汉三又回来了,快半年没更新博客了,这半年来的经历实属不易,疫情当头,本人实习的公司没有跟员工共患难, 直接辞掉了很多人.作为一个实习生,本人也被无情开除了.所以本人又得重新准备找工作了. 算 ...

  6. elasticsearch报Fielddata is disabled on text fields by default

    我刚玩elk没几天,今天启动kibana之后执行查询看见elasticsearch报了一个错误 Caused by: java.lang.IllegalArgumentException: Field ...

  7. thinkphp5.1中使用链式操作的坑

    1.在进行tp5->tp5.1的时候,没有想太多,直接使用之前的代码:结果在该操作中,多次调用该get方法,tp5.1的链式操作一直保持了之前的搜索条件,截图如下:(具体的代码没有展示) 2.然 ...

  8. web存储cookie会出现两个相同键值问题

    我使用js存储cookie,定义key值是menu,存储过程中出现了相同的menu键值而且有的时候有一个还是null,折腾了三四个小时不知道啥问题: 早上到公司后想着换换键值,就把键值换成了selec ...

  9. Girls' research(马拉车算法) hdu 3294

    文章目录 思路如下 Manachar代码注释 题解如下 Problem Description One day, sailormoon girls are so delighted that they ...

  10. 求你了,别再问我Zookeeper如何实现分布式锁了!!!

    导读 真是有人(锁)的地方就有江湖(事务),今天不谈江湖,来撩撩人. 分布式锁的概念.为什么使用分布式锁,想必大家已经很清楚了.前段时间作者写过Redis是如何实现分布式锁,今天这篇文章来谈谈Zook ...