(4)创建型模式——原型

创建型模式——原型(Prototype)

问题背景

当希望批量生成有状态对象时,应该考虑使用原型模式。假设有一种怪物,它有生命值、魔法值、攻击力、防御力等大量属性,可以一次分裂出十个与自己完全相同的怪物。如果直接实现这个逻辑,代码会是这样的:

var monster = new Monster();
monster.HP = 100;
monster.MP = 50;
monster.ATK = 50;
monster.DEF = 20;

var copies = new Monster[10];
Monster copy;
for (int i = 0; i < copies.Length; i++)
{
    copy = new Monster();
    copy.HP = monster.HP;
    copy.MP = monster.MP;
    copy.ATK = monster.ATK;
    copy.DEF = monster.DEF;
    copies[i] = copy;
}

写了一大坨没啥技术含量的代码,而且指不定就把哪个属性给写丢了,简直是费力不讨好。

解决方案

对于这种内部状态复杂,又经常需要拷贝的对象,考虑增加一个接口ICloneable,这个接口表明该对象是可复制的,并暴露复制接口,该接口以自己为“原型”创建出一个一模一样的对象并返回。类的编写者实现这个接口,就可以让使用者在不完全清楚内部状态的情况下复制该类的实例。使用原型后的程序结构是这样的:
程序结构
这样一来,Monster类的使用者只需要调用Clone接口就可以完成复制,再也不用写一大坨了!

效果

  1. 简化了对象的复制操作。
  2. 很好地隐藏了类内部的细节。
  3. 可以通过注册原型对象来实现“模板”。

缺陷

由于某些语言有“值类型”和“引用类型”之分,复制行为就会有“浅复制”和“深复制”的区别。在这些语言中,若原型模式使用不当,可能会造成一些匪夷所思的问题,这些问题一般很难被排查出来。

使用原型之前请一定一定一定要确保你知道你正在干什么。
使用原型之前请一定一定一定要确保你知道你正在干什么。
使用原型之前请一定一定一定要确保你知道你正在干什么。

相关模式

  1. 抽象工厂:原型是抽象工厂的一种实现方式。
  2. 复合:复合结构的组件一般都会使用原型。

实现

using System;

namespace Prototype
{
    class Client
    {
        public interface ICloneable
        {
            ICloneable Clone();
        }

        public class Monster : ICloneable
        {
            public int HP { get; set; }
            public int MP { get; set; }
            public int ATK { get; set; }
            public int DEF { get; set; }
            //...
            public ICloneable Clone()
            {
                var copy = new Monster();
                copy.HP = HP;
                copy.MP = MP;
                copy.ATK = ATK;
                copy.DEF = DEF;
                return copy;
            }
            public override string ToString()
            {
                return $"怪物:[HP: {HP}, MP: {MP}, ATK: {ATK}, DEF: {DEF}]";
            }
        }

        static void Main(string[] args)
        {
            var monster = new Monster();
            monster.HP = 100;
            monster.MP = 50;
            monster.ATK = 50;
            monster.DEF = 20;
            Console.WriteLine(monster);

            Console.WriteLine("分裂...");
            var copies = new Monster[10];
            for (int i = 0; i < copies.Length; i++)
            {
                copies[i] = monster.Clone() as Monster;
                Console.WriteLine(copies[i]);
            }
        }
    }
}

运行结果

发布了10 篇原创文章 · 获赞 10 · 访问量 483

猜你喜欢

转载自blog.csdn.net/DIAX_/article/details/104143771