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方法實現淺拷貝 returnthis.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方法獲取複雜屬性的副本,然後把複雜屬性副本賦值給原始物件的淺拷貝副本的導航屬性。如果導航屬性內部也有複雜屬性,重複上邊過程,這裡需要使用遞迴。