【创建型设计模式】原型设计模式

引言

我们在学习每一个设计模式之前,我们就应该带着问题去学习,这样才会找到我们想要的答案,而且让我们理解的更透彻,记忆的更深刻,比如:解决了一个什么问题,使用场景等。而最好的方法就是通过例子去敲代码去感受,去理解。

案列

供应商的信息类:供应商名称,供应商经营范围,供应商编号。现在假设我们需要复制三份供应商对象

初始代码

static void Main(string[] args)
{
    Provider provider=new Provider();
    provider.Name = "长江红公司";
    provider.Code = "NB001";
    provider.JinYinFanWei = "计算机相关硬件,服务器等等";
    provider.GetProviderData();
}

public class Provider
{
    public string Name { get; set; }    
    public string Code { get; set; }
    public string JinYinFanWei { get; set; }//经营范围



    public void SetProviderData(string mName, string mCode, string mJinYinFanWei)
    {
        this.Name= mName;
        this.Code= mCode;
        this.JinYinFanWei= mJinYinFanWei; 
    }

    public void GetProviderData() 
    {
        Console.WriteLine("编号为"+Code+":"+Name+"的供应商的经营范围:"+JinYinFanWei);
    }
}

现在我们需要复制三份

错误的做法:

 Provider provider1 = new Provider();
 Provider provider2 = new Provider();

这里虽然我们复制了三份对象,结果没问题,但是在这里provider1和provider2你只是把provider的引用传给了她们两个,在我们的内存中指向的是一块空间。而非复制了三份对象

中规中矩

static void Main(string[] args)
{
    Provider provider=new Provider();
    provider.Name = "长江红公司";
    provider.Code = "NB001";
    provider.JinYinFanWei = "计算机相关硬件,服务器等等";
    provider.GetProviderData();

    Provider provider1 = new Provider();
    provider1.Name = "长江红公司";
    provider1.Code = "NB001";
    provider1.JinYinFanWei = "计算机相关硬件,服务器等等";
    provider1.GetProviderData();

    Provider provider2 = new Provider();
    provider2.Name = "长江红公司";
    provider2.Code = "NB001";
    provider2.JinYinFanWei = "计算机相关硬件,服务器等等";
    provider2.GetProviderData();




}

但是这种写法存在问题,如果说我的需求改变了,我让你复制30份,那这样岂不是我们需要复制30分,如果说你发现这个公司的编号录入错了,那你就需要改30份,如果要复制300份呢,而且这样的代码是冗余的。

原型模式上场

原型模式的价值就是可以创建对象的同时,而且还能保证性能

通过给出一个原型对象来指明所要创建对象的类型,然后用复制这个对象的方法来创建出更多的同类型的方法

static void Main(string[] args)
{
    Provider provider = new Provider("大阳公司", "NB002", "计算机相关硬件");

    //使用浅克隆
    Provider provider1 = (Provider)provider.Clone();
    Console.WriteLine(provider1.Name);
}

public abstract class ProviderProtype
{
    public string Name { get; set; }
    public string Code { get; set; }
    public string JinYinFanWei { get; set; }//经营范围

    public ProviderProtype(string name, string code, string jinYinFanWei)
    {
        Name = name;
        Code = code;
        JinYinFanWei = jinYinFanWei;
    }

    public abstract ProviderProtype Clone();
}

public class Provider:ProviderProtype
{
    public Provider(string name, string code, string jinYinFanWei):base(name, code, jinYinFanWei)
    {

    }

    //克隆的方法
    public override ProviderProtype Clone()
    {
        //自带的浅克隆方法:
        //当我们调用该方法后,会把对象里面的值类型复制一份搞一份新的
        //值类型会创建一份新的,引用类型只会复制他的引用,而不是对象
        //而我们的深拷贝不同点就是:对引用类型的处理,深拷贝就会复制其对象
        return  (ProviderProtype)this.MemberwiseClone();
        
    }
}

其实所谓的原型模式就是实现我们的clone方法,在这个方法里面我们调用这个浅拷贝的方法,并且将其强制转换为我们返回的对象就OK了。

但是我们会发现,我们的父类在这没什么用,只是提供了一个克隆的方法,我们将其可以进行简化

static void Main(string[] args)
{
    Provider provider = new Provider();
    provider.Name = "Test"; 
    provider.Code = "NB001";
    provider.JinYinFanWei = "CESHI";

    Provider provider1 = (Provider)provider.Clone();
    Console.WriteLine(provider1.Name);
    provider1.ShowProvider();
}

///简化
//我们只需要让我们的类继承ICloneable这个接口,实现接口,再调用浅拷贝
public class Provider:ICloneable
{
    public string Name { get; set; }
    public string Code { get; set; }
    public string JinYinFanWei { get; set; }//经营范围

    public void SetProviderder(string name, string code, string jinYinFanWei)
    {
        Name = name;
        Code = code;
        JinYinFanWei = jinYinFanWei;
    }
    public void ShowProvider()
    {
        Console.WriteLine(Code+Name+JinYinFanWei );
    }

    public object Clone()
    {
        return  (Provider)this.MemberwiseClone();
    }
}

如果我们发现我们的编号写错了,我们只需要修改Porivder.code;完成以后我们的以后要复制30份那么这三份的code就已经全部改变了。但是任然会出现问题,如果我们程序里有引用类型的话,使用浅克隆是无法复制引用类型的对象。只会复制它的引用。所以我们需要深拷贝

深拷贝研究

如果我们这里只对provider2里面的setProvider调用修改了值后,会发现只修改了我的provider2,其他的竟然没修改

现在我们添加一个供应商的分类,将它设置为一个新类

static void Main(string[] args)
{
    Provider provider = new Provider();
    provider.Name = "Test"; 
    provider.Code = "NB001";
    provider.JinYinFanWei = "CESHI";

    Provider provider1 = (Provider)provider.Clone();
    Provider provider2 = (Provider)provider.Clone();
    provider2.SetProviderder("长江红","BU001","这是测试");
    provider2.SetProivderType("五金工具类");
    provider1.ShowProvider();
    provider2.ShowProvider();
}

///简化
//我们只需要让我们的类继承ICloneable这个接口,实现接口,再调用浅拷贝
public class Provider : ICloneable
{
    public string Name { get; set; }
    public string Code { get; set; }
    public string JinYinFanWei { get; set; }//经营范围

    private ProviderType providerType;

    public Provider()
    {
        this.providerType = new ProviderType();
    }

    public void SetProviderder(string name, string code, string jinYinFanWei)
    {
        Name = name;
        Code = code;
        JinYinFanWei = jinYinFanWei;
       
    }

    public void SetProivderType(string mType)
    {
        this.providerType.mType = mType;
    }
    public void ShowProvider()
    {
        Console.WriteLine(Code+Name+JinYinFanWei+ this.providerType.mType);
    }

    public object Clone()
    {
        return  (Provider)this.MemberwiseClone();
    }
}

public class ProviderType
{
    public string mType { get; set; } 
    
    
}

这里我们使用了浅拷贝以后,结果我们只改了provider2的seiProviderType但是我们的1也跟着变了,这是因为我们的浅拷贝在拷贝引用类型的时候,只是拷贝了2的引用,而不是真的对象。

我们给providerType也加上拷贝的接口,实现接口的方法,也进行拷贝

static void Main(string[] args)
{
    Provider provider = new Provider();
    provider.SetProviderder("Test", "NB001", "NB001");
    provider.SetProivderType("农业产品类");
    Provider provider1 = (Provider)provider.Clone();
    Provider provider2 = (Provider)provider.Clone();
    provider2.SetProviderder("长江红","BU001","这是测试");
    provider1.SetProivderType("数码产品类");
    provider2.SetProivderType("五金工具类");
    provider1.ShowProvider();
    provider2.ShowProvider();
}

///简化
//我们只需要让我们的类继承ICloneable这个接口,实现接口,再调用浅拷贝
public class Provider : ICloneable
{
    public string Name { get; set; }
    public string Code { get; set; }
    public string JinYinFanWei { get; set; }//经营范围

    private ProviderType providerType;

    public Provider()
    {
        this.providerType = new ProviderType();
    }

    //这个构造函数传入供应商类型类,直接copy一份
    public Provider(ProviderType mproviderType)
    {
        this.providerType = (ProviderType)mproviderType.Clone();
    }

    public void SetProviderder(string name, string code, string jinYinFanWei)
    {
        Name = name;
        Code = code;
        JinYinFanWei = jinYinFanWei;
    }

    public void SetProivderType(string mType)
    {
        this.providerType.mType = mType;
    }
    public void ShowProvider()
    {
        Console.WriteLine(Code+Name+JinYinFanWei+ this.providerType.mType);
    }

    public object Clone()
    {
        //在创建新对象的时候,把供应商类型也copy一份
        Provider provider = new Provider(this.providerType);
        provider.Name = Name;
        provider.Code = Code;
        provider.JinYinFanWei = JinYinFanWei;
        return provider;
        //如果依然调用该方法,引用类型永无出头之日
        //return  (Provider)this.MemberwiseClone();
    }
}

public class ProviderType:ICloneable
{
    public string mType { get; set; }

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

这样我们也就实现了对引用类型进行的拷贝,而不再是单单的拷贝引用类型的引用。而是对象也拷贝了。

拷贝引用类型代码梳理

我们在main方法里new provider的对像的时候,会执行他无参的构造函数,而在这个构造函数里我们把现有的this.providerType = new ProviderType();给他创建一个新对象。并且复制给我们的这个字段。

随后我们执行完构造函数,调用两个方法给我们的属性初始化。

随后执行我们最核心的一个代码:分别调用了clone的方法。

在我们的clone方法里,我们new 了个新的Provider,并且执行了私有的构造函数里,这个时候我们进入私有构造函数,我们对我们的供应商分类字段进行了拷贝。

最后我们每一个新的供应商对象,都指向了一个新的引用对象。

其实这里我们的是伪深拷贝,如果我们providerType里面的属性还是引用类型的话,那我们就是递归的再进行拷贝。如何真正的实现深拷贝呢,这就需要用到反射和我们的序列化。路漫漫其修远兮!

总结

我们的原型模式就是方便我们创建多个对象,并且提高效率

实现的方式有两种:浅拷贝和深拷贝

浅拷贝:

对于值类型是搞一个新的,对于引用类型而言只是搞一个引用

深拷贝:

不管值、引用类型都会搞一个新的

具体我们需要使用浅拷贝或者深拷贝,这就需要我们结合实际开发场景来选择。

  • 37
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值