【愚公系列】2023年10月 二十三种设计模式(四)-原型模式(Prototype Pattern)

举报
愚公搬代码 发表于 2021/12/03 22:34:44 2021/12/03
【摘要】 设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。

🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏

🚀前言

设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。

毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。

🚀一、原型模式(Prototype Pattern)

原型模式是一种创建型设计模式,它的核心思想是使用现有对象作为原型,通过复制这个原型来创建新的对象实例。

这种模式特别适用于需要大量相同或相似对象的情况,因为它可以节省对象的创建成本,避免了重复的初始化过程。在原型模式中,我们首先创建一个原型对象,然后通过对该原型对象进行复制或克隆操作,来生成新的对象副本,这些副本与原型对象具有相同或相似的属性和状态。

总的来说,原型模式关注对象的复制和克隆,它通过提供一个可被复制的原型对象来简化对象的创建过程,特别适合处理那些需要频繁创建相似对象的场景。这种模式的优点在于提高了性能和降低了资源消耗,同时也减少了与对象初始化相关的复杂性。

🚀二、使用步骤

🔎1.角色

🦋1.1 抽象原型(Prototype)

原型模式(Prototype Pattern)中的抽象原型(Prototype)是指一个抽象基类或接口,它定义了具体原型对象必须实现的方法。抽象原型通常包含一个名为clonecopy的方法,用于复制自身并创建一个新的对象副本。

抽象原型的主要作用是定义了对象复制的接口规范,为具体原型对象提供了一个通用的方法,使得客户端可以通过该方法创建新的对象,而不必关心对象创建的具体细节。具体原型类需要实现抽象原型中的clone方法,以便能够正确地复制自身并返回新的对象。

抽象原型的作用可以总结如下:

  1. 定义复制方法:抽象原型规定了具体原型类必须实现的复制方法,确保了所有原型对象都具备了复制自身的能力。

  2. 封装复制逻辑:抽象原型将复制逻辑封装在一个接口中,隐藏了具体复制细节,使客户端不需要了解对象的内部结构和复制方式。

  3. 支持多态复制:抽象原型允许不同类型的具体原型类实现不同的复制方式,从而支持多态性,客户端可以根据需要选择合适的具体原型类进行复制。

  4. 提高代码可扩展性:通过抽象原型,可以轻松扩展系统,新增不同类型的原型对象,而无需修改现有代码。

抽象原型在原型模式中充当了一个关键角色,它定义了对象复制的标准接口,使得对象的复制过程更加灵活、可扩展和封装,从而提高了代码的可维护性和可复用性。

🦋1.2 具体原型(Concrete Prototype)

原型模式(Prototype Pattern)中的具体原型(Concrete Prototype)是指实现了抽象原型(Prototype)接口的具体类。每个具体原型类都表示一种具体的对象类型,它实现了抽象原型中定义的复制方法,用于创建该类型对象的副本。

具体原型的主要作用如下:

  1. 对象复制:具体原型类实现了复制方法,可以将自身对象的状态和属性复制到一个新的对象中,从而创建该类型的新对象。这样,客户端可以通过复制原型对象来获得新的对象实例,而不必手动创建或初始化对象。

  2. 提高性能:通过复制已有对象来创建新对象,可以提高性能,尤其是当对象的创建过程比较昂贵或复杂时。因为复制通常比创建新对象的过程更加高效。

  3. 支持多态性:每个具体原型类可以实现不同的复制逻辑,从而支持多态性。客户端可以根据需要选择使用不同类型的具体原型对象进行复制。

  4. 封装对象创建细节:具体原型类封装了对象的创建和复制细节,隐藏了对象的内部结构和复制方式。客户端只需要知道如何复制原型对象,而不必关心对象的具体实现。

  5. 支持动态扩展:通过新增具体原型类,可以随时扩展系统,引入新的对象类型,而不会影响现有代码的稳定性。

具体原型是原型模式中具体对象的实现,它通过实现抽象原型接口的复制方法,允许客户端创建新对象的副本。这种模式提供了一种有效的方式来管理和创建对象,尤其适用于需要大量相似对象的情况,同时提高了代码的可扩展性和可维护性。

注:C#中的MemberwiseClone属于浅克隆。

🔎2.示例

在这里插入图片描述

在这里插入图片描述

命名空间PrototypePattern包含细胞基类Cell,它的2个实现类分别为:PlantCell植物细胞类和Animal动物细胞类,另外包含CloneBase泛型基类。本案例尝试模拟细胞的分裂过程以展示原型模式在复制对象本身方面的独到之处。

[Serializable]
public abstract class Cell : CloneBase<Cell> {

    public int Id { get; set; }

    public string Wall { get; set; }//细胞壁

    public string Membrane { get; set; }//细胞膜

    public string Cytoplasm { get; set; }//细胞质

    public string Nucleus { get; set; }//细胞核

    public Content Content { get; set; } = new Content();//细胞器

    public Cell(int id,
                string wall,
                string membrane,
                string cytoplasm,
                string nucleus) {
        this.Id = id;
        this.Wall = wall;
        this.Membrane = membrane;
        this.Cytoplasm = cytoplasm;
        this.Nucleus = nucleus;
        this.Content = new Content();
    }

    public abstract Cell Division();

}

抽象细胞基类Cell,继承自CloneBase并定义Division分裂接口。

[Serializable]
public class PlantCell : Cell {

    public PlantCell(int id,
                     string wall,
                     string membrane,
                     string cytoplasm,
                     string nucleus)
        : base(id, wall, membrane, cytoplasm, nucleus) {

    }

    public override Cell Division() {
        var cell = this.MemberwiseClone() as Cell;
        cell.Id = RandomUtil.RandomNum();
        return cell;
    }

}

植物细胞类PlantCell,细胞基类的具体实现类,标记Serializable特性以支持序列化的深克隆。

[Serializable]
public class AnimalCell : Cell {

    public AnimalCell(int id,
                      string wall,
                      string membrane,
                      string cytoplasm,
                      string nucleus)
        : base(id, wall, membrane, cytoplasm, nucleus) {

    }

    public override Cell Division() {
        var cell = this.MemberwiseClone() as Cell;
        cell.Id = RandomUtil.RandomNum();
        return cell;
    }

}

动物细胞类AnimalCell,细胞基类的具体实现类,标记Serializable特性以支持序列化的深克隆。

[Serializable]
public class Content {

    public string Mitochondria { get; set; }//线粒体

    public int Chloroplasts { get; set; }//叶绿体

    public int EndoplasmicReticulum { get; set; }//内质网

    public int GolgiBody { get; set; }//高尔基复合体

    public int Ribosomes { get; set; }//核糖体

    public int Centrosome { get; set; }//中心体

    public int Vacuole { get; set; }//液泡

    public int Lysosomes { get; set; }//溶酶体

    public int Microtubule { get; set; }//微管

}

细胞质类Content,为细胞基类中所包含的一个对象成员。

[Serializable]
public class CloneBase<T> {

    public virtual T Clone() {
        var memoryStream = new MemoryStream();
        var formatter = new BinaryFormatter();
        formatter.Serialize(memoryStream, this);
        memoryStream.Position = 0;
        return (T)formatter.Deserialize(memoryStream);
    }

}

克隆类CloneBase,包含一个虚拟的Clone方法以支持深克隆。

public class RandomUtil {

    public static int RandomNum() {
        return new Random().Next(1000000, 10000000);
    }

}

产生细胞Id的工具类,从100万到1000万。

public class Program {
 
    private static Cell _cell = null;
 
    private const string SPLIT_BREAK = "-----------------------------------------------------";
 
    public static void Main(string[] args) {
        _cell = new PlantCell(RandomUtil.RandomNum(),
                             "wall",
                             "membrane",
                             "cytoplasm",
                             "nucleus");
 
        var plant = _cell.Division();
 
        Console.WriteLine($"_cell.GUID:{_cell.Id},{Environment.NewLine}plant.GUID:{plant.Id}," +
                          $"{Environment.NewLine}equals:{_cell.Id == plant.Id}.");
 
        Console.WriteLine(SPLIT_BREAK);
 
        _cell.Content.Mitochondria = "10010101010100101010101";
 
        Console.WriteLine($"_cell.Content.Mitochondria:{_cell.Content.Mitochondria},\r\n" +
                          $"plant.Content.Mitochondria:{plant.Content.Mitochondria}," +
                          $"{Environment.NewLine}equals:" +
                          $"{_cell.Content.Mitochondria == plant.Content.Mitochondria}.");
 
        Console.WriteLine(SPLIT_BREAK);
 
        var animal = _cell.Clone();
 
        Console.WriteLine($"_cell.GUID:{_cell.Id},{Environment.NewLine}animal.GUID:{animal.Id}," +
                          $"{Environment.NewLine}equals:{_cell.Id == animal.Id}.");
 
        Console.WriteLine(SPLIT_BREAK);
 
        _cell.Content.Mitochondria = "01001010010100101010010";
 
        Console.WriteLine($"_cell.Content.Mitochondria:{_cell.Content.Mitochondria},\r\n" +
                          $"animal.Content.Mitochondria:{animal.Content.Mitochondria}," +
                          $"{Environment.NewLine}equals:" +
                          $"{_cell.Content.Mitochondria == animal.Content.Mitochondria}.");
 
        Console.WriteLine(SPLIT_BREAK);
 
        Console.ReadKey();
    }
 
}

以上是调用方的代码,植物细胞实例调用了浅克隆,而动物细胞实例调用了深克隆,请仔细分析这段代码。以下是这个案例的输出结果:

_cell.GUID:6768270,
plant.GUID:2028096,
equals:False.
-----------------------------------------------------
_cell.Content.Mitochondria:10010101010100101010101,
plant.Content.Mitochondria:10010101010100101010101,
equals:True.
-----------------------------------------------------
_cell.GUID:6768270,
animal.GUID:6768270,
equals:True.
-----------------------------------------------------
_cell.Content.Mitochondria:01001010010100101010010,
animal.Content.Mitochondria:10010101010100101010101,
equals:False.
-----------------------------------------------------

🚀总结

🔎1.优点

原型模式(Prototype Pattern)具有以下优点:

  1. 对象复制性能优异:通过复制现有对象来创建新对象,避免了对象的初始化过程,因此可以大幅提高对象的创建性能,尤其是当对象的初始化过程复杂或昂贵时。

  2. 简化对象创建:客户端只需通过复制原型对象即可创建新对象,无需手动创建或初始化,使得对象创建过程更加简洁和易用。

  3. 隐藏对象创建细节:抽象了对象的创建和复制过程,客户端不必了解对象的内部结构和创建方式,降低了系统的耦合度。

  4. 动态扩展性:可以通过新增具体原型类来引入新类型的对象,扩展性良好,不影响现有代码的稳定性。

  5. 多态复制:支持多态性,不同的具体原型类可以实现不同的复制逻辑,根据需要选择合适的具体原型进行复制。

  6. 提高系统性能:避免了重复初始化对象的开销,通过复制已有对象提高了系统性能,特别是在大规模对象创建时。

  7. 保持对象状态:允许对象保持一定的状态,可以在创建新对象时传递一部分状态,降低了对象间的耦合度。

原型模式通过对象的复制来创建新对象,极大地提高了对象创建的效率和灵活性,适用于需要大量相似对象的场景,同时可以降低系统的开销,提高性能。

🔎2.缺点

原型模式(Prototype Pattern)虽然具有许多优点,但也存在一些缺点,包括:

  1. 对象构造复杂性:如果对象的构造过程比较复杂,包括多个依赖关系和资源初始化,那么复制对象可能会变得困难,因为需要在复制时精确地处理这些依赖关系和资源。

  2. 深拷贝问题:原型模式默认执行浅拷贝(即只复制对象的基本属性),如果需要深拷贝(复制对象及其嵌套对象),则需要额外的处理,这可能增加了实现的复杂性。

  3. 破坏封装性:如果具体原型类的内部结构发生变化,可能会影响到克隆对象的正确性,破坏了封装性和隔离性。

  4. 需要额外的初始化:原型模式虽然避免了对象的构造过程,但在使用复制后的对象之前,仍然需要适当地初始化一些属性或状态,否则可能会导致不正确的行为。

  5. 不适用于所有对象:并不是所有的对象都适合使用原型模式,只有当需要创建大量相似对象且对象的初始化成本较高时才有意义。对于简单的对象,使用原型模式可能会显得繁琐。

  6. 内存消耗大:如果大量的原型对象被复制并存在于内存中,可能会导致内存消耗较大,需要谨慎管理对象的复制和销毁。

原型模式在某些场景下非常有用,但也需要注意其局限性和潜在的复杂性。在使用原型模式时,需要根据具体的需求和对象的复杂性来权衡其优点和缺点。

🔎3.使用场景

原型模式(Prototype Pattern)适用于以下场景:

  1. 大量相似对象的创建:当需要创建大量具有相似属性和状态的对象时,使用原型模式可以显著减少对象创建的开销。例如,图形设计软件中的绘图工具可以使用原型模式来创建各种形状对象。

  2. 复杂对象的创建:当对象的构造过程非常复杂,包括多个依赖关系和资源初始化时,使用原型模式可以避免重复执行这些复杂的构造过程,提高性能。

  3. 对象的状态变化较小:如果对象的状态只有一小部分会发生变化,而大部分状态保持不变,那么可以使用原型模式来创建对象的副本,并只修改变化的状态,从而节省资源。

  4. 动态配置对象:原型模式可以用于动态配置对象,客户端可以复制一个预定义的原型对象,并根据需要修改其属性,以快速创建不同配置的对象。

  5. 对象的初始化代价高:如果对象的初始化过程涉及到大量资源加载、数据库连接、网络请求等耗时操作,使用原型模式可以减少这些操作的重复执行。

  6. 支持多态性:原型模式允许不同类型的具体原型类实现不同的复制逻辑,从而支持多态性,客户端可以根据需要选择不同类型的原型对象。

  7. 保持对象的封装性:原型模式可以帮助保持对象的封装性,客户端无需了解对象的具体创建方式和内部结构。

原型模式在需要大量相似对象、复杂对象创建、对象状态变化较小、动态配置对象等情况下非常有用。它可以提高性能、降低资源消耗,并提高代码的灵活性和可维护性。


🚀感谢:给读者的一封信

亲爱的读者,

我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。

如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。

我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。

如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。

在这里插入图片描述

再次感谢您的阅读和支持!

最诚挚的问候, “愚公搬代码”

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。