05、C# 设计模式-原型模式

原型模式(Prototype Pattern)

原型模式属于创建型模式,使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

原型模式关注的是大量相同或相似对象的创建问题。应用原型模式就是建立一个原型,然后通过对原型来进行复制的方法,产生一个和原型相同或相似的新对象。

角色:

1、 抽象原型(Prototype);

声明一个克隆自身的接口,通常名为Clone;

2、 具体原型(ConcretePrototype);

实现一个克隆自身的操作,包含深拷贝和浅拷贝。

浅克隆(Shallow Clone):当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制,它们只是指向同一个引用。
深克隆(Deep Clone):除了对象本身被复制外,对象所包含的所有成员变量也将被复制,它们拥有不同的副本。

注:C#中的MemberwiseClone属于浅克隆。

示例:

 

 

命名空间PrototypePattern包含细胞基类Cell,它的2个实现类分别为:PlantCell植物细胞类和Animal动物细胞类,另外包含CloneBase泛型基类。本案例尝试模拟细胞的分裂过程以展示原型模式在复制对象本身方面的独到之处。

namespace PrototypePattern
[Serializable]
public abstract class Cell : CloneBase<Cell> {

    public int Id { get; set; }

    public string Wall { get; set; }//细胞壁

    public string Membrane { get; set; }//细胞膜

    public string Cytoplasm { get; set; }//细胞质

    public string Nucleus { get; set; }//细胞核

    public Content Content { get; set; } = new Content();//细胞器

    public Cell(int id,
                string wall,
                string membrane,
                string cytoplasm,
                string nucleus) {
        this.Id = id;
        this.Wall = wall;
        this.Membrane = membrane;
        this.Cytoplasm = cytoplasm;
        this.Nucleus = nucleus;
        this.Content = new Content();
    }

    public abstract Cell Division();

}

抽象细胞基类Cell,继承自CloneBase并定义Division分裂接口。

[Serializable]
public class PlantCell : Cell {

    public PlantCell(int id,
                     string wall,
                     string membrane,
                     string cytoplasm,
                     string nucleus)
        : base(id, wall, membrane, cytoplasm, nucleus) {

    }

    public override Cell Division() {
        var cell = this.MemberwiseClone() as Cell;
        cell.Id = RandomUtil.RandomNum();
        return cell;
    }

}

植物细胞类PlantCell,细胞基类的具体实现类,标记Serializable特性以支持序列化的深克隆。

[Serializable]
public class AnimalCell : Cell {

    public AnimalCell(int id,
                      string wall,
                      string membrane,
                      string cytoplasm,
                      string nucleus)
        : base(id, wall, membrane, cytoplasm, nucleus) {

    }

    public override Cell Division() {
        var cell = this.MemberwiseClone() as Cell;
        cell.Id = RandomUtil.RandomNum();
        return cell;
    }

}

动物细胞类AnimalCell,细胞基类的具体实现类,标记Serializable特性以支持序列化的深克隆。

[Serializable]
public class Content {

    public string Mitochondria { get; set; }//线粒体

    public int Chloroplasts { get; set; }//叶绿体

    public int EndoplasmicReticulum { get; set; }//内质网

    public int GolgiBody { get; set; }//高尔基复合体

    public int Ribosomes { get; set; }//核糖体

    public int Centrosome { get; set; }//中心体

    public int Vacuole { get; set; }//液泡

    public int Lysosomes { get; set; }//溶酶体

    public int Microtubule { get; set; }//微管

}

细胞质类Content,为细胞基类中所包含的一个对象成员。

[Serializable]
public class CloneBase<T> {

    public virtual T Clone() {
        var memoryStream = new MemoryStream();
        var formatter = new BinaryFormatter();
        formatter.Serialize(memoryStream, this);
        memoryStream.Position = 0;
        return (T)formatter.Deserialize(memoryStream);
    }

}

克隆类CloneBase,包含一个虚拟的Clone方法以支持深克隆。

public class RandomUtil {

    public static int RandomNum() {
        return new Random().Next(1000000, 10000000);
    }

}

产生细胞Id的工具类,从100万到1000万。

public class Program {

    private static Cell _cell = null;

    private const string SPLIT_BREAK =
        "-----------------------------------------------------";

    public static void Main(string[] args) {
        _cell = new PlantCell(RandomUtil.RandomNum(),
                             "wall",
                             "membrane",
                             "cytoplasm",
                             "nucleus");

        var plant = _cell.Division();

        Console.WriteLine($"_cell.GUID:{_cell.Id},{Environment.NewLine}plant.GUID:{plant.Id}," +
                          $"{Environment.NewLine}equals:{_cell.Id == plant.Id}.");

        Console.WriteLine(SPLIT_BREAK);

        _cell.Content.Mitochondria = "10010101010100101010101";

        Console.WriteLine($"_cell.Content.Mitochondria:{_cell.Content.Mitochondria},\r\n" +
                          $"plant.Content.Mitochondria:{plant.Content.Mitochondria}," +
                          $"{Environment.NewLine}equals:" +
                          $"{_cell.Content.Mitochondria == plant.Content.Mitochondria}.");

        Console.WriteLine(SPLIT_BREAK);

        var animal = _cell.Clone();

        Console.WriteLine($"_cell.GUID:{_cell.Id},{Environment.NewLine}animal.GUID:{animal.Id}," +
                          $"{Environment.NewLine}equals:{_cell.Id == animal.Id}.");

        Console.WriteLine(SPLIT_BREAK);

        _cell.Content.Mitochondria = "01001010010100101010010";

        Console.WriteLine($"_cell.Content.Mitochondria:{_cell.Content.Mitochondria},\r\n" +
                          $"animal.Content.Mitochondria:{animal.Content.Mitochondria}," +
                          $"{Environment.NewLine}equals:" +
                          $"{_cell.Content.Mitochondria == animal.Content.Mitochondria}.");

        Console.WriteLine(SPLIT_BREAK);

        Console.ReadKey();
    }

}

以上是调用方的代码,植物细胞实例调用了浅克隆,而动物细胞实例调用了深克隆,请仔细分析这段代码。以下是这个案例的输出结果:

_cell.GUID:6768270,
plant.GUID:2028096,
equals:False.
-----------------------------------------------------
_cell.Content.Mitochondria:10010101010100101010101,
plant.Content.Mitochondria:10010101010100101010101,
equals:True.
-----------------------------------------------------
_cell.GUID:6768270,
animal.GUID:6768270,
equals:True.
-----------------------------------------------------
_cell.Content.Mitochondria:01001010010100101010010,
animal.Content.Mitochondria:10010101010100101010101,
equals:False.
-----------------------------------------------------

优点:

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

缺点:

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

使用场景:

1、 当一个系统应该独立于它的产品创建、构成和表示时;
2、 当要实例化的类是在运行时刻指定时,例如通过动态装载来创建一个类;
3、 为了避免创建一个与产品类层次平行的工厂类层次时;
4、 当一个类的实例只能有几个不同状态组合中的一种时建立相应数目的原型并Clone它们可能比每次用合适的状态手工实例化该类更方便一些;