設計模式 5/23 原型模式

原型模式,一個深刻淺出,檢驗你對基礎知識瞭解的是否透徹的一個設計模式。html

之因此這樣定義,由於我栽了個跟頭面試

想要吃透原型模式,就得深刻理解 淺拷貝,深拷貝設計模式

想要深刻吃透 淺拷貝,深拷貝ide

咱們就要對 值類型 和 引用類型 有較深的認識函數

若是對 值類型 和 引用類型 有了較深的認識,在GC也至少有必定的修爲了性能

..........我還能繼續這樣寫下去不少,因此有開頭的那句話,原型模式,一個深刻淺出,檢驗你對基礎知識瞭解的是否透徹的一個設計模式。this

 

對值類型 和 引用類型 的淺薄的 入門,能夠參考 幾年前寫的 面試前的準備---C#知識點回顧----02spa

裏面有一些基礎知識的介紹,能夠做爲了解 淺拷貝 和 深拷貝的鋪墊設計

 

重點來了,看黑板!!!code

深拷貝:指的是拷貝一個對象時,不只僅把對象的引用進行復制,還把該對象引用的值也一塊兒拷貝。這樣進行深拷貝後的拷貝對象就和源對象互相獨立,其中任何一個對象的改動都不會對另一個對象形成影響。舉個例子,你有一張光盤,你進行了屢次刻錄,產生了第二張,第三者光盤,那這三種光盤是相互獨立的,第一張損壞或第二張損壞,都不會影響到第三張【光盤,會不會暴露年齡啊】。在.NET領域,值對象就是典型的例子,如int, Double以及結構體和枚舉等。具體例子以下所示:

             int source = 11;
            // 值類型賦值內部執行深拷貝
            int copy = source;
            // 對拷貝對象進行賦值不會改變源對象的值
            copy = 22;//此時source仍然爲11
            // 一樣對源對象賦值也不會改變拷貝對象的值
            source = 33;//此時copy 仍然爲22            
View Code

 怎麼實現深拷貝,能夠最原始的一個一個賦值,但也能夠用反射或反序列化方式來實現,因人而異,具體根據具體需求定,尋找代碼最合適的方法

 

淺拷貝:指的是拷貝一個對象時,僅僅拷貝對象的引用進行拷貝,可是拷貝對象和源對象仍是引用同一份實體。此時,其中一個對象的改變都會影響到另外一個對象。其實咱們本身常常淺拷貝,在家,你是個寶寶的身份,在學校,你是學生的身份,在公司面前,你是職員,但若是你生病了,那麼你全部身份都會生病,全都反應在你這個實體身上。在.NET中引用類型就是一個例子。如類類型。具體例子以下所示:

 Person you = new Person() { Info = "You" };
            Person schoolP = you; // 淺拷貝
            schoolP.Info = "學生"; // 拷貝對象改變Info值
            Person familyP = you;
            familyP.Info = "寶寶";
            Person emp = you;
            emp.Info = "職員";
            // 忽然你生病了
            you.Info = "生病了";
            Console.WriteLine("schoolP.Info: {0};familyP.Info: {1};emp.Info:{2}", schoolP.Info, familyP.Info, emp.Info);
            Console.Read();


public class Person
{
    public string Info { get; set; }
}
View Code

 

重點!!!在C#中,淺拷貝的實現方式很簡單,.NET自身也提供了實現,只須要實現接口 : ICloneable

.NET中值類型默認是深拷貝的,而對於引用類型,默認實現的是淺拷貝。因此對於類中引用類型的屬性改變時,其另外一個對象也會發生改變。當某個類的實例有個字段是值類型,那麼實際該字段會和類的實例保持在同一個地方,即堆上

這次載的跟頭就是在,讓我痛定思痛的記住了,類都是引用類型

 

有了前面的鋪墊,咱們來看看原型模式

原型模式:用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象

重點再次來臨,2點

1.前半句告訴你原型還得本身建

2.後半句指點你,剩下的對象能夠經過拷貝實現。具體是深拷貝仍是淺拷貝,根據你的需求來定

舉個例子,來品味下原型模式

在 War3 ,魔獸爭霸[再次暴露年齡], 大海龜地圖中有4個點位有野怪,這些野怪組成由 1個大海龜,2個小海龜組成,都是海龜,他們的差異是傷害輸出的不一樣。

咱們來看看怎麼畫這個圖

先定義野怪,由於在地圖中,除了海歸,還有其餘野怪

    /// <summary>
    /// 敵人,野怪
    /// </summary>
    [Serializable]
    public abstract class Enemy : ICloneable
    {
        protected Enemy(Location location, int power, int speed)
        {
            Location = location;
            Power = power;
            Speed = speed;
        }

        /// <summary>
        /// 出生點位
        /// </summary>
        public Location Location { get; set; }

        /// <summary>
        /// 攻擊力[1~10 愈來愈強]
        /// </summary>
        public int Power { get; set; }

        /// <summary>
        /// 行動速度[1~10 愈來愈快]
        /// </summary>
        public int Speed { get; set; }

        /// <summary>
        /// 深拷貝
        /// </summary>
        /// <returns></returns>
        public abstract Enemy DeepClone();

        public abstract void Show();

        /// <summary>
        /// 淺拷貝
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }
View Code

這是出生點位類

    /// <summary>
    /// 出生點位
    /// </summary>
    [Serializable]
    public class Location
    {
        /// <summary>
        /// 橫座標
        /// </summary>
        public int X;

        /// <summary>
        /// 縱座標
        /// </summary>
        public int Y;

        public Location(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
View Code

再定義海歸野怪

    /// <summary>
    /// 海龜
    /// </summary>
    [Serializable]
    public class SeaTurtle : Enemy
    {
        public SeaTurtle(Location location, int power, int speed)
            : base(location, power, speed)
        {
        }

        public override Enemy DeepClone()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            // 序列化成流
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            // 反序列化成對象
            SeaTurtle seaTurtle = (SeaTurtle)formatter.Deserialize(memoryStream);
            return seaTurtle;
        }

        public override void Show()
        {
            Console.WriteLine("我是海龜,攻擊力:{0},速度:{1},出生點位:{2},{3}", Power, Speed, Location.X, Location.Y);
        }
    }
View Code

好比咱們如今要生成一組野怪,咱們看看怎麼用原型模式實現

static void Main()
        {
            Location seaTurtleLocation = new Location(2, 2);

            //大海龜出生
            SeaTurtle bigSeaTurtle = new SeaTurtle(seaTurtleLocation, 10, 10);

            //照着大海龜 拷貝一個小海龜  A 
            Enemy smallSeaTurtleA = (SeaTurtle)bigSeaTurtle.DeepClone();
            smallSeaTurtleA.Location.X = seaTurtleLocation.X + 1;
            smallSeaTurtleA.Power = bigSeaTurtle.Power / 2;

            //再直接copy小海龜A 拷貝一個小海龜  B   
            Enemy smallSeaTurtleB = (SeaTurtle)smallSeaTurtleA.DeepClone();
            smallSeaTurtleB.Location.X = seaTurtleLocation.X - 1;

            bigSeaTurtle.Show();
            smallSeaTurtleA.Show();
            smallSeaTurtleB.Show();
            Console.ReadLine();

        }
View Code

最終的結果

我是海龜,攻擊力:10,速度:10,出生點位:2,2
我是海龜,攻擊力:5,速度:10,出生點位:3,2
我是海龜,攻擊力:5,速度:10,出生點位:1,2

我在Enemy類中實現了接口ICloneable,你們能夠試試用Clone方法會獲得什麼效果 

 

總結下

優勢:

用於建立重複的對象,同時又能保證性能,同時,咱們還能夠不用構造方法

缺點

一、配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不必定很容易,特別當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。

二、逃避了構造函數的約束,若是咱們的構造方法有約定,那由於能夠不用到構造方法,因此咱們逃避了約定

 

以上就是關於 原型模式 的分享

大家的支持是我寫做的動力源泉,請不要吝嗇你的點贊,謝謝

相關文章
相關標籤/搜索