原型模式(prototype pattern)

        原型模式其实很简单,又可以成为克隆模式。想象你要造一架飞机,如果你要重头到位掌握造飞机的核心技术,那么你要从力学,材料学开始学起。但是你想快点看到成果,那你就把对应的零件买回来自己组装一个就好了。

目录

1.UML图

2. 深拷贝与浅拷贝

3.案例

4.小结

4.1 优点

4.2 缺点

 4.3 使用场景


1.UML图

        

 可以看出,在原型模式的结构图有以下角色:
(1)、原型类(Prototype):原型类,声明一个Clone自身的接口;
(2)、具体原型类(ConcretePrototype):实现一个Clone自身的操作。
在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。

        所以关键在如何实现Clone()函数。

2. 深拷贝与浅拷贝

         相比大家都知道深拷贝与浅拷贝的区别,这里不过多讲述了。只是在C#里面系统提供了一个函数:

protected object MemberwiseClone ();

 我们先来讲解一下这个函数,再给出案例。这个函数是一个浅拷贝函数,该方法 MemberwiseClone 通过创建新对象,然后将当前对象的非静态字段复制到新对象来创建浅表副本。 如果字段是值类型,则执行字段的逐位副本。 如果字段是引用类型,则会复制引用,但引用对象不是;因此,原始对象及其克隆引用同一对象。

官方的例子其实很典型:

using System;

public class IdInfo
{
    public int IdNumber;

    public IdInfo(int IdNumber)
    {
        this.IdNumber = IdNumber;
    }
}

public class Person
{
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    public Person ShallowCopy()
    {
       return (Person) this.MemberwiseClone();
    }

    public Person DeepCopy()
    {
       Person other = (Person) this.MemberwiseClone();
       other.IdInfo = new IdInfo(IdInfo.IdNumber);
       other.Name = String.Copy(Name);
       return other;
    }
}

public class Example
{
    public static void Main()
    {
        // Create an instance of Person and assign values to its fields.
        Person p1 = new Person();
        p1.Age = 42;
        p1.Name = "Sam";
        p1.IdInfo = new IdInfo(6565);

        // Perform a shallow copy of p1 and assign it to p2.
        Person p2 = p1.ShallowCopy();

        // Display values of p1, p2
        Console.WriteLine("Original values of p1 and p2:");
        Console.WriteLine("   p1 instance values: ");
        DisplayValues(p1);
        Console.WriteLine("   p2 instance values:");
        DisplayValues(p2);

        // Change the value of p1 properties and display the values of p1 and p2.
        p1.Age = 32;
        p1.Name = "Frank";
        p1.IdInfo.IdNumber = 7878;
        Console.WriteLine("\nValues of p1 and p2 after changes to p1:");
        Console.WriteLine("   p1 instance values: ");
        DisplayValues(p1);
        Console.WriteLine("   p2 instance values:");
        DisplayValues(p2);

        // Make a deep copy of p1 and assign it to p3.
        Person p3 = p1.DeepCopy();
        // Change the members of the p1 class to new values to show the deep copy.
        p1.Name = "George";
        p1.Age = 39;
        p1.IdInfo.IdNumber = 8641;
        Console.WriteLine("\nValues of p1 and p3 after changes to p1:");
        Console.WriteLine("   p1 instance values: ");
        DisplayValues(p1);
        Console.WriteLine("   p3 instance values:");
        DisplayValues(p3);
    }

    public static void DisplayValues(Person p)
    {
        Console.WriteLine("      Name: {0:s}, Age: {1:d}", p.Name, p.Age);
        Console.WriteLine("      Value: {0:d}", p.IdInfo.IdNumber);
    }
}
// The example displays the following output:
//       Original values of p1 and p2:
//          p1 instance values:
//             Name: Sam, Age: 42
//             Value: 6565
//          p2 instance values:
//             Name: Sam, Age: 42
//             Value: 6565
//
//       Values of p1 and p2 after changes to p1:
//          p1 instance values:
//             Name: Frank, Age: 32
//             Value: 7878
//          p2 instance values:
//             Name: Sam, Age: 42
//             Value: 7878
//
//       Values of p1 and p3 after changes to p1:
//          p1 instance values:
//             Name: George, Age: 39
//             Value: 8641
//          p3 instance values:
//             Name: Frank, Age: 32
//             Value: 7878

IdInfo是一个引用类型,所以浅拷贝是拷贝的是它的引用,当原对象中的IdInfo改变时,浅拷贝对象也会改变。

        所以当你实现Clone函数时,要注意那些是值对象,那些是引用对象!

3.案例

        这里我构造了一个Person抽象类:重点关注ShallowClone(),和DeepClone().

    internal abstract class Person
    {
        public string? Name { get; set; }
        public int? Age { get; set; }
        public Address? Address { get; set; }

        public abstract Person? ShallowClone();

        public abstract Person? DeepClone();
    }

然后有一个具体的Custome类派生自person,并额外多了字段:

internal class Custome:Person
    {
        public Order? Order { get; set; }
        public Custome(string name,Address address)
        {
            Name = name;
            Address = address;
        }
        public void SetOrder(Order order)=>Order = order;
        public override Custome? ShallowClone()
        {
            return this.MemberwiseClone() as Custome;
        }
        public override Custome? DeepClone()
        {
            Custome custome = (Custome)MemberwiseClone();
            custome.Address = new Address( Address?.Country ?? "China", Address?.City ?? "Beijing");
            custome.Order = Order?.Clone();
            return custome;
        }
        public void Show()
        {
            Console.WriteLine($"Name:   {Name}");
            Console.WriteLine($"Age:     {Age}");
            Console.WriteLine($"Address;   {Address?.Country} ,{Address?.City}");
            Console.WriteLine($"Order:  \n \t at: { Order?.Time} " +
                $"\t goods: {Order?.Goods.Aggregate((x, y) => x + "  " + y)}");
        }
    }

ShallowClone函数没啥好说的了,直接调用MemberWiseClone完事。重点看DeepClone函数。

这里Adress和Order都是引用类型,其中Adresss是通过构造函数创建新对象,Order则不是,我们为Order实现了一个clone函数,专门用于创建对象。

 internal class Order
    {
        public Guid Guid { get; set; }
        public DateTime Time { get; set; }
        public List<string> Goods { get; set; } = new List<string>();
        public Order()
        {
            Guid = Guid.NewGuid();
            Time = DateTime.Now;
        }
        
        public Order? Clone()
        {
            //不能直接返回对象o,yinwei Goods是引用类型,修改Goods会影响到克隆对象里面
            var o= MemberwiseClone() as Order;
            o.Goods=Goods.ToList();  
            return o;
        }
        public void AddGood(string item)=>Goods.Add(item);
    }

要注意Goods是引用类型,所以不能直接调用MemberWiseClone

Address就没啥特别了:

    internal class Address
    {
        public string Country { get; set; }
        public string City { get; set; }
        public Address(string country,string city)
        {
            Country = country;
            City = city;
        }
    }

最后简单测试一下:

Address address = new Address("美国", "纽约");
Custome c1 = new Custome("川普", address);
Order Order = new Order();
Order.AddGood("Chip");
Order.AddGood("Gun");
c1.SetOrder(Order);
c1.Show();
var c2=c1.ShallowClone();
var c3=c1.DeepClone();  
c2?.Show();
c3?.Show();
var o1 = Order.Clone();
c1?.Order?.AddGood("软中华");
c1.Age = 20;
address.City = "华盛顿";
c1.Show();
c2?.Show();
c3?.Show();

4.小结

4.1 优点

 (1)、原型模式向客户隐藏了创建新实例的复杂性
(2)、原型模式允许动态增加或较少产品类。
(3)、原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
(4)、产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构

4.2 缺点

(1)、每个类必须配备一个克隆方法
(2)、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 

 4.3 使用场景

 1)、资源优化场景
  类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
(2)、性能和安全要求的场景
  通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
(3)、一个对象多个修改者的场景
  一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。


完整代码:PatternDesignReview

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值