1. 程式人生 > >C#設計模式(6)——原型模式

C#設計模式(6)——原型模式

1.原型模式介紹

  在軟體系統開發中,有時候會遇到這樣的情況:我們需要用到多個相同例項,最簡單直接的方法是通過多次呼叫new方法來建立相同的例項。如下:

Person person=new Person(){Name="jack",Age=20};
Person person2=new Person(){Name="jack",Age=20};
Person person3=new Person(){Name="jack",Age=20};

  但是有一個問題,如果我用要使用的例項建立起來十分耗費資源,或者建立起來步驟比較繁瑣,上邊的程式碼缺點就暴露出來了:耗費資源,每次建立例項都要重複繁瑣的建立過程。原始模式可以很好地解決這個問題,使用原型模式我們不需要每次都new一個新的例項,而是通過拷貝原有的物件來完成建立

,這樣我們就不需要在記憶體中建立多個物件,也不需要重複複雜的建立過程了。下邊以克隆人為例解釋原型模式的用法,程式碼非常簡單。

人類原型類,提供了一個克隆抽象方法:

   /// <summary>
    /// 人類原型抽象類
    /// </summary>
    public abstract class PersonPrototype 
    {
        public abstract object clone();
    }

人類繼承原型類,表示人類可以被克隆,每個人都有名字,年齡和住址:

   /// <summary>
/// Person /// </summary> public class Person: PersonPrototype { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } public override object clone() { //MemberwiseClone方法實現淺拷貝 return
this.MemberwiseClone(); } } /// <summary> /// 住址 /// </summary> public class Address { public string Province { get; set; } public string City { get; set; } }

客戶端程式碼:

        static void Main(string[] args)
        {
            //建立一個person例項
            Person person1 = new Person()
            {
                Name = "jack",
                Age = 20,
                Address = new Address{Province = "山東",City = "青島"}
            };
            //一個克隆人
            Person clonePerson = (Person)person1.clone();

            Console.WriteLine($"person1的name:{person1.Name},年齡:{person1.Age}," +
                              $"省份:{person1.Address.Province},城市:{person1.Address.City}");
            Console.WriteLine($"克隆人的name:{clonePerson.Name},年齡:{clonePerson.Age}," +
                              $"省份:{clonePerson.Address.Province},城市:{clonePerson.Address.City}");
            Console.ReadKey();
        }

執行結果如下:

我們可以看到,通過clone方法成功獲取了一個相同的person例項。

這裡需要注意一點:通過object.MemberWiseClone()獲取一個物件的例項屬於淺拷貝,對例項的簡單型別屬性進行全值拷貝(包含string型別),對複雜型別屬性只拷貝了引用。客戶端程式碼如下

 

       static void Main(string[] args)
        {
            //建立一個person例項
            Person person1 = new Person()
            {
                Name = "jack",
                Age = 20,
                Address = new Address{Province = "山東", City = "青島"}
            };
            //一個克隆人
            Person clonePerson = (Person)person1.clone();
            clonePerson.Name = "tom";
            clonePerson.Age = 22;
            clonePerson.Address.Province = "浙江";
            clonePerson.Address.City = "杭州";


            Console.WriteLine($"person1的name:{person1.Name},年齡:{person1.Age}," +
                              $"省份:{person1.Address.Province},城市:{person1.Address.City}");
            Console.WriteLine($"克隆人的name:{clonePerson.Name},年齡:{clonePerson.Age}," +
                              $"省份:{clonePerson.Address.Province},城市:{clonePerson.Address.City}");
            Console.ReadKey();
        }
    }

執行結果:對於複雜型別Address,修改clonePerson的省份和城市,person1的Address也修改了。

修改clonePerson的名字,person1的名字沒有變,這一點是MemberwishClone方法和直接賦值的區別,我們修改客戶端程式碼,將

       static void Main(string[] args)
        {
            //建立一個person例項
            Person person1 = new Person()
            {
                Name = "jack",
                Age = 20,
                Address = new Address{Province = "山東",City = "青島"}
            };
            //這裡使用直接賦值,而不是clone
            Person clonePerson = person1;
            clonePerson.Name = "tom";
            clonePerson.Age = 22;
            clonePerson.Address.Province = "浙江";
            clonePerson.Address.City = "杭州";


            Console.WriteLine($"person1的name:{person1.Name},年齡:{person1.Age}," +
                              $"省份:{person1.Address.Province},城市:{person1.Address.City}");
            Console.WriteLine($"克隆人的name:{clonePerson.Name},年齡:{clonePerson.Age}," +
                              $"省份:{clonePerson.Address.Province},城市:{clonePerson.Address.City}");
            Console.ReadKey();
        }

執行結果:直接賦值修改字串型別的屬性(name)原始例項也會修改

 2.小結

上邊例子的類圖

原型模式的優點:

  1.隱藏了建立例項的繁瑣過程,只需通過Clone方法就可以獲取例項物件

  2.使用淺拷貝替代new,減少資源消耗

原型模式的缺點:

  1.需要新增一個Clone方法,C#中一般使用MemberwishClone方法來獲取例項的淺拷貝副本。

補充:如果想實現深拷貝常用的有兩種方法:①將原始例項序列化,然後反序列化賦值給副本物件;②將原始物件的複雜屬性通過clone方法獲取複雜屬性的副本,然後把複雜屬性副本賦值給原始物件的淺拷貝副本的導航屬性。如果導航屬性內部也有複雜屬性,重複上邊過程,這裡需要使用遞迴。