c#設計模式-原型模式


意圖

  用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

  場景

  游戲場景中的有很多相似的敵人,它們的技能都一樣,但是隨着敵人出現的位置不同,這些人的能力不太一樣。假設,我們現在需要把三個步兵組成一隊,其中還有一個精英步兵,能力特別高。那么,你或許可以創建一個敵人抽象類,然后對於不同能力的步兵創建不同的子類。然后,使用工廠方法等設計模式讓調用方依賴敵人抽象類。

  問題來了,如果有無數種能力不同步兵,難道需要創建無數子類嗎?還有,步兵模型的初始化工作是非常耗時的,創建這么多步兵對象可能還會浪費很多時間。我們是不是可以通過只創建一個步兵原型,然后復制出多個一摸一樣的步兵呢?復制后,只需要調整一下這些對象在地圖上出現的位置,或者調整一下它們的能力即可。原型模式就是用來解決這個問題的。

  示例代碼

  

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM