原型模式

DoubleJ

什么是原型模式?

原型模式是一种对象创建型模式,它将一个原型对象传递给一个要发起创建的对象,该对象通过请求原型对象复制自己来实现创建过程。

由于软件系统中经常会出现需要创建多个相同或相似的对象,因此原型模式在开发中具有较高的使用频率。通过原型模式克隆出来的对象属于全新的对象,它们在内存中拥有自己的内存地址,对克隆对象的修改不会影响原型对象,并且每一个克隆对象都是相互独立的。

浅克隆与深克隆

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将会复制一份给克隆对象;如果成员变量是引用类型,则将引用类型的地址复制一份给克隆对象,也就是说克隆对象与原型对象的成员变量指向相同的内存地址。即浅克隆的时候,只复制原型对象本身和它的值类型成员,而引用类型的成员并没有复制。

深克隆

深克隆的时候,原型对象中的值类型成员和引用类型成员都将复制给克隆对象,即深克隆除了复制对象本身以外,对象所包含的所有成员也将被复制。

原型模式的结构

image.png

  • ProtoType(抽象原型类):声明克隆方法的接口,所有具体原型类的父类,可以是抽象类或者接口。
  • ProtoTypeA(具体原型类):继承自ProtoType并实现克隆方法,在克隆方法中返回一个自己的克隆对象。
  • Client(使用类):使用的时候让一个原型对象克隆自身创建一个新的对象,只需要创建出一个原型对象,就可以通过调用原型对象的克隆方法即可得到多个相同的对象。

原型模式的实现

abstract class ProtoType
{
    public abstract ProtoType Clone();
}

class ProtoTypeA : ProtoType
{
    private string m_Attr;
    public string Attr
    {
        get
        {
            return this.m_Attr;
        }

        set
        {
            this.m_Attr = value;
        }
    }
    
    public override ProtoType Clone()
    {
        var o = new ProtoTypeA();
        o.Attr = this.m_Attr;
        return o;
    }
}

static void Main()
{
    //使用
    var prototypeA = new ProtoTypeA();
    var copy = prototypeA.Clone();
}

C#的MemberwiseClone方法

在C#语言中,提供了MemberwiseClone方法用于实现浅克隆,修改代码如下:

class ProtoTypeA : ProtoType
{
    private string m_Attr;
    public string Attr
    {
        get
        {
            return this.m_Attr;
        }

        set
        {
            this.m_Attr = value;
        }
    }
    
    private Member m_Member;
    public Member Member
    {
        get
        {
            return this.m_Member;
        }

        set
        {
            this.m_Member = value;
        }
    }
        
    public override ProtoType Clone()
    {
        return (ProtoTypeA)this.MemberwiseClone();
    }
}

调用以下代码证明为浅克隆

static void Main()
{
    var prototypeA = new ProtoTypeA();
    var copy = (ProtoTypeA)prototypeA.Clone();
    Console.WriteLine(prototypeA == copy); //false
    Console.WriteLine(prototypeA.Attr == copy.Attr); //true
    Console.WriteLine(prototypeA.Member == copy.Member); //true,证明为浅克隆
    Console.ReadKey();
}

修改代码实现深克隆

public override ProtoType Clone()
{
    var copy = (ProtoTypeA)this.MemberwiseClone();
    copy.Member = new Member();
    return copy;
}

static void Main()
{
    var prototypeA = new ProtoTypeA();
    var copy = (ProtoTypeA)prototypeA.Clone();
    Console.WriteLine(prototypeA == copy); //false
    Console.WriteLine(prototypeA.Attr == copy.Attr); //true
    Console.WriteLine(prototypeA.Member == copy.Member); //false,证明为深克隆
    Console.ReadKey();
}

序列化实现深克隆

[Serializable]
abstract class ProtoType
{
    public abstract ProtoType Clone();
}
    
[Serializable]
class Member { }

[Serializable]
class ProtoTypeA : ProtoType
{
    ...
}

修改Clone方法

public override ProtoType Clone()
{
    ProtoTypeA copy = null;
    using (var fs = new FileStream("Temp.txt", FileMode.Create))
    {
        try
        {
            new BinaryFormatter().Serialize(fs, this);
        }
        catch (SerializationException e)
        {

            throw e;
        }
    }

    using (var fs = new FileStream("Temp.txt", FileMode.Open))
    {
        try
        {
            copy = (ProtoTypeA)new BinaryFormatter().Deserialize(fs);
        }
        catch (SerializationException e)
        {

            throw e;
        }
    };
    return copy;
}

static void Main()
{
    var prototypeA = new ProtoTypeA();
    prototypeA.Member = new Member();
    var copy = (ProtoTypeA)prototypeA.Clone();
    Console.WriteLine(prototypeA == copy); //false
    Console.WriteLine(prototypeA.Attr == copy.Attr); //true
    Console.WriteLine(prototypeA.Member == copy.Member); //false,证明为深克隆
    Console.ReadKey();
}

原型模式的优点&缺点

优点

  • 如果要创建的对象实例较为复杂,使用原型模式可以简化对象的创建过程,提高新实例的创建效率。
  • 较好的扩展性,原型模式中提供了抽象原型类,针对抽象编程,可将具体原型类写入配置文件。
  • 简化了创建结构,与工厂方法模式不同,无需专门的工厂类来创建对象。
  • 可以使用深克隆的方法保存对象的状态,使用原型模式将对象复制一份并将其状态保存,以便在需要用的时候使用,例如恢复到某一历史状态。

缺点

  • 实现深克隆时需要编写较为复杂的代码,当对象之间存在多重嵌套引用时,每一层对象都必须支持深克隆,实现起来会比较麻烦,增加系统理解难度。

适合使用原型模式的场景

  • 创建对象成本较大(耗时长、占用CPU资源或网络资源大),新对象可以通过复制已有对象获得,若是相似对象,则可以修改其成员变量即可。
  • 系统需要保存对象的状态。
阅读 249
7 声望
2 粉丝
0 条评论
你知道吗?

7 声望
2 粉丝
文章目录
宣传栏