设计模式之创建型(6)-原型模式(Prototype)

设计模式之创建型(6)-原型模式(Prototype)

(一)定义:Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

1.1  UML类图

image

1.2  类与对象之间的关系

Prototype:抽象原型基类,定义具有克隆自己的方法的接口。

ConcretePrototype:具体原型类,实现具体克隆方法。

Client:客户程序,通过克隆生成一个新的对象。

1.3  典型调用顺序图

image

代码public abstract class Prototype
{
    private string id;
    public Prototype(string id)
    {
        this.id = id;
    }
    public string ID
    {
        get
        {
            return this.id;
        }
    }
    public abstract Prototype Clone();
}

public class ConcretePrototype1 : Prototype
{
    private System.Data.DataTable dt = new System.Data.DataTable();
    public ConcretePrototype1(string id)
        : base(id)
    {
    }

    public override Prototype Clone()
    {
        return (Prototype)this.MemberwiseClone();
    }
}

public class Client
{
    ConcretePrototype1 p1 = null;
    ConcretePrototype1 c1 = null;
    public Client()
    {
        p1 = new ConcretePrototype1("p1");
        c1 = (ConcretePrototype1)p1.Clone();
    }

    public override string ToString()
    {
        bool isbool = Object.ReferenceEquals(p1, c1);
        return string.Format("p1.id={0} HashCode={1}\r\nc1.id={2} HashCode={3}", p1.ID, p1.GetHashCode(), c1.ID, c1.GetHashCode());
    }
}

(二) 原型模式中的浅拷贝和深拷贝(C#语言实现)

2.1  new操作做了什么

new操作符可以创建值类型变量、引用类型对象,同时自动调用构造函数。

例子:假设有一个Point类,有x,y两个变量。以下动画演示了在new操作过程中的内存分配的过程。其中的内存布局图如下:

456[6]image

从以上图内存布局图示可以看到p中存储的是一个指向Point对象的地址值。当调用new操作符时会在内存中分配连续地址来建立Point类的内存空间。然后将首地址空间赋值给指向的变量。

假定现有一个Rect类,由两个点组成。

代码public class Point
{
    public int x;
    public int y;
    public Point()
    {

    }
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

public class Rect
{
    public Point startPoint = null;
    public Point endPoint = null;
    public Rect()
    {
        startPoint = new Point(0, 0);
        endPoint = new Point(10, 10);
    }
}

其内存变化如下图:

789         image

2.2  程序运行的内存区域

一个程序将操作系统分配给其运行的内存块分为4个区域:

1)代码区:存放程序的代码,即程序中的各个函数代码块。

2)全局数据区:存放程序的全局数据和静态数据。

3)堆区:存放程序的动态数据。在C/C++中,用alloc系统函数和new申请的内存都存在于堆中。在C#语言中有堆和托管堆的区别。

4)栈区:存放程序的局部数据,即各个函数中的数据。

2.3  Object.MemberwiseClone()方法的使用

MemberwiseClone()方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
例如,考虑一个名为 X 的对象,该对象引用对象 A 和 B。对象 B 又引用对象 C。X 的浅表副本创建一个新对象 X2,该对象也引用对象 A 和 B。与此相对照,X 的深层副本创建一个新对象 X2,该对象引用新对象 A2 和 B2,它们分别是 A 和 B 的副本。B2 又引用新对象 C2,C2 是 C 的副本。使用实现 ICloneable 接口的类执行对象的浅表或深层复制。

浅表拷贝代码public class X
{
    public A a = null;
    public B b = null;
    public X()
    {
        a = new A();
        b = new B();
    }
    public X Clone()
    {
        return (X)this.MemberwiseClone();
    }
}
public class A
{
    public A()
    {

    }
}
public class B
{
    public C c = null;
    public B()
    {
        c = new C();
    }

}
public class C
{
    public C()
    {

    }
}

 

111111111111111111111111

 

无标题

2.4  深拷贝的几种实现方式

    2.4.1:利用MemberwiseClone()方法特性(.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。)

代码演示public class X : ICloneable
{
    public A a = null;
    public B b = null;

    public X()
    {
        a = new A();
        b = new B();
    }
    private X(A a, B b)
    {
        this.a = (A)a.Clone();
        this.b = (B)b.Clone();
    }

    public object Clone()
    {
        //浅拷贝
        //return this.MemberwiseClone(); 
        //深拷贝
        X x = new X(this.a, this.b);
       return x;
    }
}

public class A : ICloneable
{
    private string a = "";
    public A()
    {
    }

    public string PA
    {
        get { return this.a; }
        set { this.a = value; }
    }

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}
public class B : ICloneable
{
    private string b = "";
    public C c = null;
    public B()
    {
        c = new C();
    }
    public string PB
    {
        get { return this.b; }
        set { this.b = value; }
    }

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}
public class C : ICloneable
{
    private string c = "";
    public C()
    {

    }
    public string PC
    {
        get { return this.c; }
        set { this.c = value; }
    }
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

代码调试动画分析:从代码调试看出:实现Clone方法后创建得到的对象a中存储的字段值还是“123”当再次赋值为“456”时x2对象的字段值改变,没有影响到x1对象的“123”值。以上代码存在的问题是如果此时对C类的字段c进行赋值改变会同时影响到x1,x2对象的值(如右图)

只需在B中添加和修改以下代码即可:(.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。)

以上方式存在的问题:如果类间的依赖关系太复杂,则每个与之相关的类成员变量都需要实现Clone方法进行非值类型的深拷贝。

代码private B(C c)
{
    this.c = (C)c.Clone();
}
public object Clone()
{
    //B b = new B(c);
    return new B(c);
    //return this.MemberwiseClone();
}

88888 66666

    2.4.2:利用反射原理实现深拷贝

 

    2.4.3

posted @ 2014-05-25 00:00 李华丽 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值