Edward_jie

for you, my Hall of Frame

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

意图

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

  场景

  游戏场景中的有很多相似的敌人,它们的技能都一样,但是随着敌人出现的位置不同,这些人的能力不太一样。假设,我们现在需要把三个步兵组成一队,其中还有一个精英步兵,能力特别高。那么,你或许可以创建一个敌人抽象类,然后对于不同能力的步兵创建不同的子类。然后,使用工厂方法等设计模式让调用方依赖敌人抽象类。

  问题来了,如果有无数种能力不同步兵,难道需要创建无数子类吗?还有,步兵模型的初始化工作是非常耗时的,创建这么多步兵对象可能还会浪费很多时间。我们是不是可以通过只创建一个步兵原型,然后复制出多个一摸一样的步兵呢?复制后,只需要调整一下这些对象在地图上出现的位置,或者调整一下它们的能力即可。原型模式就是用来解决这个问题的。

  示例代码

  

View Code
//using System;
//using System.Collections;
//using System.IO;
//using System.Runtime.Serialization;
//using System.Runtime.Serialization.Formatters.Binary;


//[Serializable]
//abstract class ColorPrototype
//{
//    public abstract ColorPrototype Clone(bool Deep);
//}
//[Serializable]
//class ConcteteColorPrototype : ColorPrototype
//{
//    private int _red, _green, _blue;
//    public ConcteteColorPrototype(int red, int green, int blue)
//    {
//        this._red = red;
//        this._green = green;
//        this._blue = blue;
//    }
//    public override ColorPrototype Clone(bool Deep)
//    {
//        if (Deep)
//            return CreateDeepCopy();
//        else
//            return (ColorPrototype)this.MemberwiseClone();
//    }
//    //实现深拷贝
//    public ColorPrototype CreateDeepCopy()
//    {
//        ColorPrototype colorPrototype;
//        MemoryStream memoryStream = new MemoryStream();
//        BinaryFormatter formatter = new BinaryFormatter();
//        formatter.Serialize(memoryStream, this);
//        memoryStream.Position = 0;
//        colorPrototype = (ColorPrototype)formatter.Deserialize(memoryStream);
//        return colorPrototype;
//    }
//    public ConcteteColorPrototype Create(int red, int green, int blue)
//    {
//        return new ConcteteColorPrototype(red, green, blue);
//    }
//    public void Display(string _colorname)
//    {
//        Console.WriteLine("{0}'s RGB Values are: {1},{2},{3}",
//            _colorname, _red, _green, _blue);
//    }
//}
//class ColorManager
//{
//    Hashtable colors = new Hashtable();
//    public ColorPrototype this[string name]
//    {
//        get
//        {
//            return (ColorPrototype)colors[name];
//        }
//        set
//        {
//            colors.Add(name, value);
//        }
//    }
//}



//class App
//{
//    public static void Main(string[] args)
//    {
//        ColorManager colormanager = new ColorManager();
//        //初始化颜色
//        colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);
//        colormanager["green"] = new ConcteteColorPrototype(0, 255, 0);
//        colormanager["blue"] = new ConcteteColorPrototype(0, 0, 255);
//        colormanager["angry"] = new ConcteteColorPrototype(255, 54, 0);
//        colormanager["peace"] = new ConcteteColorPrototype(128, 211, 128);
//        colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20);
//        //使用颜色
//        string colorName = "red";
//        ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);
//        c1.Display(colorName);
//        colorName = "peace";
//        ConcteteColorPrototype c2 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);
//        c2.Display(colorName);
//        colorName = "flame";
//        ConcteteColorPrototype c3 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);
//        c3.Display(colorName);
//        Console.ReadLine();
//    }
//}
using System;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
namespace PrototypeExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            GameScene gs = new GameScene();
            //Enemy enemyPrototype = new FootMan(5, 4, new Location(100, 200));
            //List<Enemy> enemyGroup = gs.CreateEnemyGroup(enemyPrototype); 

            Enemy enemyPrototype = new FootMan(5, 4, new Location(100, 200));
            Enemy benemyPrototype = new FootMan(5, 4, new Location(100, 200));
            Enemy cenemyPrototype = new FootMan(5, 4, new Location(100, 200));
            List<Enemy> enemyGroup = new List<Enemy>();
            enemyGroup.Add(enemyPrototype);
            enemyGroup.Add(benemyPrototype);
            enemyGroup.Add(cenemyPrototype);
            foreach (FootMan ft in enemyGroup)
            {
                ft.ShowInfo();
                ft.FootmanAttack();
            }
            Console.WriteLine(sw.ElapsedMilliseconds);
        }
    }
    class GameScene
    {
        public List<Enemy> CreateEnemyGroup(Enemy enemyPrototype)
        {
            List<Enemy> enemyGroup = new List<Enemy>();
            Enemy e1 = enemyPrototype.Clone(true);
            e1.Location.x = enemyPrototype.Location.x - 10;
            Enemy e2 = enemyPrototype.Clone(true);
            e2.Location.x = enemyPrototype.Location.x + 10;
            Enemy elite = enemyPrototype.Clone(true);
            elite.Power = enemyPrototype.Power * 2;
            elite.Speed = enemyPrototype.Speed * 2;
            elite.Location.x = enemyPrototype.Location.x;
            elite.Location.y = enemyPrototype.Location.y + 10;
            enemyGroup.Add(e1);
            enemyGroup.Add(e2);
            enemyGroup.Add(elite);
            return enemyGroup;
        }
    }
    [Serializable]
    class Location
    {
        public int x;
        public int y;
        public Location(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    [Serializable]
    abstract class Enemy
    {
        protected Location location;
        public Location Location
        {
            get { return location; }
            set { location = value; }
        }
        protected int power;
        public int Power
        {
            get { return power; }
            set { power = value; }
        }
        protected int speed;
        public int Speed
        {
            get { return speed; }
            set { speed = value; }
        }
        public abstract Enemy Clone(bool isDeepCopy);
        public abstract void ShowInfo();
        public Enemy(int power, int speed, Location location)
        {
            Thread.Sleep(1000); // Construct method is assumed to be a high calc work. 
            this.power = power;
            this.speed = speed;
            this.location = location;
        }
    }
    [Serializable]
    class FootMan : Enemy
    {
        private string model;
        public FootMan(int power, int speed, Location location)
            : base(power, speed, location)
        {
            model = "footman";
        }
        public override void ShowInfo()
        {
            Console.WriteLine("model:{0} power:{1} speed:{2} location:({3},{4})", model, power, speed, location.x, location.y);
        }
        public override Enemy Clone(bool isDeepCopy)
        {
            FootMan footman;
            if (isDeepCopy)
            {
                MemoryStream memoryStream = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(memoryStream, this);
                memoryStream.Position = 0;
                footman = (FootMan)formatter.Deserialize(memoryStream);
            }
            else
                footman = (FootMan)this.MemberwiseClone();
            return footman;
        }
        public void FootmanAttack()
        {
            Console.WriteLine("FootmanAttack");
        }
    }
} 

 

  代码执行结果如下图:

  代码说明

  Enemy类是抽象原型,它有两个用途,一是定义了原型的一些抽象内容,二是定义了原型模式必须的拷贝方法。在这里,我们看到,每个敌人的属性有位置、攻击力、速度等,并且能通过ShowInfo()方法来获取这个人的信息。

  FootMan类就是具体原型了,它显示了敌人的具体参数以及实现了克隆自身。

  GameScene类就是调用方,在这里我们并没有看到有和具体原因进行依赖,通过复制传入的克隆原型,得到一些新的敌人,在原型的基础上稍微调整一下就变成了一支敌人部队。

  原型模式通过对原型进行克隆来替代无数子类,因此也就减少了调用方和具体类型产生依赖的程序。

  Clone()方法接受一个参数,表示是否是深拷贝。在这里,我们通过序列化反序列化实现深拷贝,深拷贝实现对象的完整复制,包括对象内部的引用类型都会复制一份全新的。在这里,如果3个敌人对象的Location都指向内存同一个地址的话,那么它们就分不开了,因此,在复制的时候需要进行深拷贝,使得它们的Location是独立的。

  在初始化Enemy的时候,我们Sleep()了一下,目的是模拟对象的创建是一个非常耗时的工作,这也体现了原型模式的另一个优势,在生成敌人的时候,我们其实无需再做这些工作了,我们只需要得到它的完整数据,并且进行一些修改就是一个新的敌人。

  运行程序后可以看到,虽然创建了三个敌人,但是只耗费了一个敌人的创建时间,三个敌人都是从原型克隆出来的。由于进行了深拷贝,修改了一个敌人的位置并不会影响其它敌人。

  何时采用

  从代码角度来说, 如果你希望运行时指定具体类(比如是使用Footman作为敌人还是使用其它),或者你希望避免创建对象时的初始化过程(如果这个过程占用的时间和资源都非常多),或者是希望避免使用工厂方法来实现多态的时候,可以考虑原型模式。

  从应用角度来说, 如果你创建的对象是多变化、多等级的产品,或者产品的创建过程非常耗时的时候(比如,有一定的计算量,或者对象创建时需要从网络或数据库中获取一定的数据),或者想把产品的创建独立出去,不想了解产品创建细节的时候可以考虑使用。不得不说,原型模式给了我们多一种创建对象,并且不依赖具体对象的选择。

  实现要点

  .NET中使用Object的MemberwiseClone()方法来实现浅拷贝,通过序列化和反序列化实现深拷贝,后者代价比较大,选择何时的拷贝方式。

  原型模式同样需要抽象类型和具体类型,通过相对稳定的抽象类型来减少或避免客户端的修改可能性。

  在代码中,我们把敌人作为了抽象类型,抽象层次很高。完全可以把步兵作为抽象类型,下面有普通步兵,手榴弹步兵等等,再有一个坦克作为抽象类型,下面还有普通坦克和防导弹坦克。这样GameScene可能就需要从两种抽象类型克隆出许多步兵和坦克。不管怎么样抽象,只要是对象类型由原型实例所指定,新对象通过原型实例做拷贝,那么这就是原型模式。

  注意事项

  注意选择深拷贝和浅拷贝。

  拷贝原型并进行修改意味着原型需要公开更多的数据,对已有系统实现原型模式可能修改的代价比较大。

 

更新日期:2007-10-22 16:33  出处:博客园  作者:LoveCherry

posted on 2012-06-01 14:37  Edward_诺  阅读(3102)  评论(1编辑  收藏  举报