Prototype 原型(创建型模式)

依赖关系的倒置

抽象不应该依赖于实现细节,实现细节应该依赖于抽象。

– 抽象A直接依赖于实现细节b

image 

 

–抽象A依赖于抽象B,实现细节b依赖于抽象B

image

 

动机(Motivation)

      在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
      如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象” ,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?

 

意图(Intent)
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。——《设计模式》GoF

 

结构(Structure)

image

 

 

  实例代码

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace testWpf
{
    
/// <summary>
    
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
    
/// </summary>
    [Serializable]
    
public class NormalActor
    {
        
//跟.Net的Clone不一样
        public virtual NormalActor Clone()
        {
            
/*
             * MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
             * 如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;
             * 因此,原始对象及其复本引用同一对象。
             * 因此这里使用了对象序列化和反序列化的方式得到一个深拷贝对象
             
*/
            MemoryStream stream 
= new MemoryStream();
            BinaryFormatter formatter 
= new BinaryFormatter(); 
            formatter.Serialize(stream, 
this);
            stream.Position 
= 0;
            
return formatter.Deserialize(stream) as NormalActor;
        }
    }

    
/// <summary>
    
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
    
/// </summary>
    [Serializable]
    
public class FlyActor
    {
        
//跟.Net的Clone不一样
        public virtual FlyActor Clone()
        {
            MemoryStream stream 
= new MemoryStream();
            BinaryFormatter formatter 
= new BinaryFormatter();
            formatter.Serialize(stream, 
this);
            stream.Position 
= 0;
            
return formatter.Deserialize(stream) as FlyActor;
        }
    }

    
/// <summary>
    
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
    
/// </summary>
    [Serializable]
    
public class WaterActor
    {
        
//跟.Net的Clone不一样
        public virtual WaterActor Clone()
        {
            MemoryStream stream 
= new MemoryStream();
            BinaryFormatter formatter 
= new BinaryFormatter();
            formatter.Serialize(stream, 
this);
            stream.Position 
= 0;
            
return formatter.Deserialize(stream) as WaterActor;
        }
    }

    
/// <summary>
    
/// 虽然父类NormalActor已经是声明了可序列化标识“[Serializable]”,
    
/// 但“[Serializable]”是不可继承的,所以子类如果要可以序列化,则还是要加上[Serializable]标识
    
/// </summary>
    [Serializable]
    
public class NormalActorA : NormalActor
    {
    }

    [Serializable]
    
public class NormalActorB : NormalActor
    {
    }

    [Serializable]
    
public class FlyActorA : FlyActor
    {
       
    }

    [Serializable]
    
public class FlyActorB : FlyActor
    {
    }

    [Serializable]
    
public class WaterActorA : WaterActor
    {
    }

    [Serializable]
    
public class WaterActorB : WaterActor
    {
    }

    
public class GameSystem
    {
        
public NormalActor normalActor;
        
public FlyActor flyActor;
        
public WaterActor waterActor; 

        
public void Run()
        {
            NormalActor normalActor1 
= normalActor.Clone();
            NormalActor normalActor2 
= normalActor.Clone();
            NormalActor normalActor3 
= normalActor.Clone();
            NormalActor normalActor4 
= normalActor.Clone();
            NormalActor normalActor5 
= normalActor.Clone();

            FlyActor flyActor1 
= flyActor.Clone();
            FlyActor flyActor2 
= flyActor.Clone();

            WaterActor waterActor1 
= waterActor.Clone();
            WaterActor waterActor2 
= waterActor.Clone();
        }
    }

    
public class App
    {
        
public static void Main()
        {
            GameSystem gameSystem 
= new GameSystem();
            gameSystem.normalActor 
= new NormalActorA();
            gameSystem.flyActor 
= new FlyActorB();
            gameSystem.waterActor 
= new WaterActorA();
            gameSystem.Run();
        }
    }
}

 

 

Prototype模式的几个要点

  1. Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
  2. Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
  3. Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。

 

有关创建性模式的讨论

  1. Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。
  2. Factory Method, Abstract Factory, Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。
  3. 如果遇到“易变类”,起初的设计通常从FactoryMethod开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式( Abstract Factory,Builder , Prototype )。

最后谈谈使用Prototype模式需要使用到的两个技术问题:MemberwiseClone与Clone,[Serializable]属性能否继承的问题

一、MemberwiseClone与Clone

       MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

下面的代码就是演示这个问题:

代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace testWpf
{
    [Serializable]    
    
class DemoClass    
    {        
        
public int i = 0;
        
public int[] iArr = { 123 };
        
public DemoClass Clone1()        
        {            
            
return this.MemberwiseClone() as DemoClass;        
        }        
        
        
public DemoClass Clone2()        
        {           
            MemoryStream stream 
= new MemoryStream();
            BinaryFormatter formatter 
= new BinaryFormatter();
            formatter.Serialize(stream, 
this);
            stream.Position 
= 0;
            
return formatter.Deserialize(stream) as DemoClass;
        }    
    }    
    
class Program    
    {        
        
static void Main(string[] args)
        {            
            DemoClass a 
= new DemoClass();
            a.i 
= 10;            
            a.iArr 
= new int[] { 8910 };
            DemoClass b 
= a.Clone1();            
            DemoClass c 
= a.Clone2();            // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化              
            a.iArr[0= 88;            
            Console.WriteLine(
"MemberwiseClone");            
            Console.WriteLine(b.i);            
            
foreach (var item in b.iArr)            
            {                
                Console.WriteLine(item);            
            }            
            Console.WriteLine(
"Clone2"); 
            Console.WriteLine(c.i);       
            
foreach (var item in c.iArr)    
            {          
                Console.WriteLine(item);
            }        
            Console.ReadLine();  
        }  
    }
}

 

 

 

 二、[Serializable]属性能否继承的问题

代码
    [Serializable]
    
public abstract class CSMessage : MessageBase
    {
        
private string userName;
        
protected CSMessage(string anUserName)
        {
            userName 
= anUserName;
        }

        
public string UserName
        {
            
get { return userName; }
        }

    }


    [Serializable]
    
public class LoginMessage : CSMessage
    {
        
private string password;
        
public LoginMessage(string userName, string password)
            : 
base(userName)
        {
            
this.password = password;
        }
        
public string Password
        {
            
get { return password; }
        }
    }

 

代码如上,测试发现类属性是不可以继承的,仔细推敲MSDN上关于对类属性是说明,类属性是一种标记当代码被编译为MSIL后由CLR根据类属性标记为其它附上相关的特性,唉。。不仔细看还是很容易弄错的

posted on 2009-12-14 22:32  一粒沙  阅读(224)  评论(0编辑  收藏  举报