原型模式(Prototype Pattern)
概述
在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化?
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
结构图
Prototype模式结构图
生活中的例子
Prototype模式使用原型实例指定创建对象的种类。新产品的原型通常是先于全部产品建立的,这样的原型是被动的,并不参与复制它自己。一个细胞的有丝分裂,产生两个同样的细胞,是一个扮演主动角色复制自己原型的例子,这演示了原型模式。一个细胞分裂,产生两个同样基因型的细胞。换句话说,细胞克隆了自己。
使用细胞分裂例子的Prototype模式对象图
原型模式解说
我们考虑这样一个场景,假定我们要开发一个调色板,用户单击调色板上任一个方块,将会返回一个对应的颜色的实例,下面我们看看如何通过原型模式来达到系统动态加载具体产品的目的。
很自然,我们利用OO的思想,把每一种颜色作为一个对象,并为他们抽象出一个公用的父类,如下图:
实现代码:
public abstract class Color
{
public abstract void Display();
}
public class RedColor:Color
{
public override void Display()
{
Console.WriteLine("Red's RGB Values are:255,0,0");
}
}
public class GreenColor:Color
{
public override void Display()
{
Console.WriteLine("Green's RGB Values are:0,255,0");
}
}
客户程序需要某一种颜色的时候,只需要创建对应的具体类的实例就可以了。但是这样我们并没有达到封装变化点的目的,也许你会说,可以使用工厂方法模式,为每一个具体子类定义一个与其等级平行的工厂类,那么好,看一下实现:
实现代码:
public abstract class ColorFactory
{
public abstract Color Create();
}
public class RedFactory:ColorFactory
{
public override Color Create()
{
return new RedColor();
}
}
public class GreenFactory:ColorFactory
{
public override Color Create()
{
return new GreenColor();
}
}
实现了这一步之后,可以看到,客户程序只要调用工厂方法就可以了。似乎我们用工厂方法模式来解决是没有问题的。但是,我们考虑的仅仅是封装了new变化,而没有考虑颜色的数量是不断变化的,甚至可能是在程序运行的过程中动态增加和减少的,那么用这种方法实现,随着颜色数量的不断增加,子类的数量会迅速膨大,导致子类过多,显然用工厂方法模式有些不大合适。
进一步思考,这些Color子类仅仅在初始化的颜色对象类别上有所不同。添加一个ColorTool这样的类,来参数化的它的实例,而这些实例是由Color支持和创建的。我们让ColorTool通过克隆或者拷贝一个Color子类的实例来创建新的Color,这个实例就是一个原型。如下图所示:
实现代码:
abstract class ColorPrototype
{
public abstract ColorPrototype Clone();
}
class ConcteteColorPrototype : ColorPrototype
{
private int _red, _green, _blue;
public ConcteteColorPrototype(int red, int green, int blue)
{
this._red = red;
this._green = green;
this._blue = blue;
}
public override ColorPrototype Clone()
{
//实现浅拷贝
return (ColorPrototype) this.MemberwiseClone();
}
public void Display(string _colorname)
{
Console.WriteLine("{0}'s RGB Values are: {1},{2},{3}",
_colorname,_red, _green, _blue );
}
}
class ColorManager
{
Hashtable colors = new Hashtable();
public ColorPrototype this[string name]
{
get
{
return (ColorPrototype)colors[name];
}
set
{
colors.Add(name,value);
}
}
}
现在我们分析一下,这样带来了什么好处?首先从子类的数目上大大减少了,不需要再为每一种具体的颜色产品而定一个类和与它等级平行的工厂方法类,而ColorTool则扮演了原型管理器的角色。再看一下为客户程序的实现:
class App
{
public static void Main(string[] args)
{
ColorManager colormanager = new ColorManager();
//初始化颜色
colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);
colormanager["green"] = new ConcteteColorPrototype(0, 255, 0);
colormanager["blue"] = new ConcteteColorPrototype(0, 0, 255);
colormanager["angry"] = new ConcteteColorPrototype(255, 54, 0);
colormanager["peace"] = new ConcteteColorPrototype(128, 211, 128);
colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20);
//使用颜色
string colorName = "red";
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone();
c1.Display(colorName);
colorName = "peace";
ConcteteColorPrototype c2 = (ConcteteColorPrototype)colormanager[colorName].Clone();
c2.Display(colorName);
colorName = "flame";
ConcteteColorPrototype c3 = (ConcteteColorPrototype)colormanager[colorName].Clone();
c3.Display(colorName);
Console.ReadLine();
}
}
可以看到,客户程序通过注册原型实例就可以将一个具体产品类并入到系统中,在运行时刻,可以动态的建立和删除原型。最后还要注意一点,在上面的例子中,用的是浅表复制。如果想做深复制,需要通过序列化的方式来实现。经过了上面的分析之后,我们再来思考下面的问题:
1.为什么需要Prototype模式?
引入原型模式的本质在于利用已有的一个原型对象,快速的生成和原型对象一样的实例。你有一个A的实例a:A a = new A();现在你想生成和car1一样的一个实例b,按照原型模式,应该是这样:A b = a.Clone();而不是重新再new一个A对象。通过上面这句话就可以得到一个和a一样的实例,确切的说,应该是它们的数据成员是一样的。Prototype模式同样是返回了一个A对象而没有使用new操作。
2.引入Prototype模式带来了什么好处?
可以看到,引入Prototype模式后我们不再需要一个与具体产品等级结构平行的工厂方法类,减少了类的构造,同时客户程序可以在运行时刻建立和删除原型。
3.Prototype模式满足了哪些面向对象的设计原则?
依赖倒置原则:上面的例子,原型管理器(ColorManager)仅仅依赖于抽象部分(ColorPrototype),而具体实现细节(ConcteteColorPrototype)则依赖与抽象部分(ColorPrototype),所以Prototype很好的满足了依赖倒置原则。
通过序列化实现深拷贝
要实现深拷贝,可以通过序列化的方式。抽象类及具体类都必须标注为可序列化的[Serializable],上面的例子加上深拷贝之后的完整程序如下:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
abstract class ColorPrototype
{
public abstract ColorPrototype Clone(bool Deep);
}
[Serializable]
class ConcteteColorPrototype : ColorPrototype
{
private int _red, _green, _blue;
public ConcteteColorPrototype(int red, int green, int blue)
{
this._red = red;
this._green = green;
this._blue = blue;
}
public override ColorPrototype Clone(bool Deep)
{
if(Deep)
return CreateDeepCopy();
else
return (ColorPrototype) this.MemberwiseClone();
}
//实现深拷贝
public ColorPrototype CreateDeepCopy()
{
ColorPrototype colorPrototype;
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
colorPrototype = (ColorPrototype)formatter.Deserialize(memoryStream);
return colorPrototype;
}
public ConcteteColorPrototype Create(int red,int green,int blue)
{
return new ConcteteColorPrototype(red,green,blue);
}
public void Display(string _colorname)
{
Console.WriteLine("{0}'s RGB Values are: {1},{2},{3}",
_colorname,_red, _green, _blue );
}
}
class ColorManager
{
Hashtable colors = new Hashtable();
public ColorPrototype this[string name]
{
get
{
return (ColorPrototype)colors[name];
}
set
{
colors.Add(name,value);
}
}
}
class App
{
public static void Main(string[] args)
{
ColorManager colormanager = new ColorManager();
//初始化颜色
colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);
colormanager["green"] = new ConcteteColorPrototype(0, 255, 0);
colormanager["blue"] = new ConcteteColorPrototype(0, 0, 255);
colormanager["angry"] = new ConcteteColorPrototype(255, 54, 0);
colormanager["peace"] = new ConcteteColorPrototype(128, 211, 128);
colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20);
//使用颜色
string colorName = "red";
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);
c1.Display(colorName);
colorName = "peace";
ConcteteColorPrototype c2 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);
c2.Display(colorName);
colorName = "flame";
ConcteteColorPrototype c3 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);
c3.Display(colorName);
Console.ReadLine();
}
}
实现要点
1.使用原型管理器,体现在一个系统中原型数目不固定时,可以动态的创建和销毁,如上面的举的调色板的例子。
2.实现克隆操作,在.NET中可以使用Object类的MemberwiseClone()方法来实现对象的浅表拷贝或通过序列化的方式来实现深拷贝。
3.Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。
效果
1.它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。
2.Prototype模式允许客户只通过注册原型实例就可以将一个具体产品类并入到系统中,客户可以在运行时刻建立和删除原型。
3.减少了子类构造,Prototype模式是克隆一个原型而不是请求工厂方法创建一个,所以它不需要一个与具体产品类平行的Creater类层次。
4.Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。
5.产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构
6.Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
适用性
在下列情况下,应当使用Prototype模式:
1.当一个系统应该独立于它的产品创建,构成和表示时;
2.当要实例化的类是在运行时刻指定时,例如,通过动态装载;
3.为了避免创建一个与产品类层次平行的工厂类层次时;
4.当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
总结
Prototype模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,达到了“隔离类对象的使用者和具体类型(易变类)之间的耦合关系”的目的。
参考文献
《设计模式》(中文版)
《DesignPatternsExplained》
《Java与模式》(阎宏 著)
分享到:
相关推荐
**原型模式(Prototype Pattern)**是一种基于克隆的创建型设计模式,它的主要目的是为了提高创建新对象的效率,特别是当创建新对象的过程复杂或者资源消耗较大时。在原型模式中,一个已经创建的对象(称为原型)被...
原型模式(Prototype Pattern)是一种创建型设计模式,允许通过复制现有对象来创建新对象,而不是通过类构造器。这种模式常用于需要频繁创建相似对象的场景,能够提高性能并减少内存使用。 原型模式的组成 原型接口...
代理模式(Proxy Pattern)、单例模式(Singleton Pattern)、工厂方法模式...原型模式(Prototype Pattern)、中介者模式(Mediator Pattern)、解释器模式(Interpreter Pattern)、享元模式(Flyweight Pattern)、...
**原型设计模式(Prototype Pattern)**是一种创建型设计模式,它允许我们通过复制现有的对象来创建新对象,而不是通过构造函数来实例化新对象。在面向对象编程中,当我们需要频繁地创建具有相同或相似属性的对象时,...
(二) 确保对象的唯一性——单例模式 (三) 确保对象的唯一性——单例模式 (四) 确保对象的唯一性——单例模式 (五) 原型模式-Prototype Pattern 对象的克隆——原型模式(一) 对象的克隆——原型模式(二) ...
原型模式(Prototype Pattern)是其中一种行为设计模式,它允许我们通过复制现有对象来创建新对象,而不是通过传统方式实例化新对象。这一模式尤其适用于创建复杂或昂贵的对象,因为它提供了对象克隆的能力,从而...
原型模式(Prototype Pattern)是一种创建型设计模式,允许通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象。在需要创建大量相似对象时非常有用,它可以避免重复创建对象,从而提高性能,并且可以...
本文将详细探讨"原型设计模式"(Prototype Design Pattern)及其在iOS中的应用。 原型设计模式是一种创建型设计模式,它通过复制已有对象来创建新对象,而不是通过构造函数或者工厂方法。这种模式主要用于减少创建...
在软件设计中,原型模式是一种创建型设计模式,它提供了一种通过复制已有对象来创建新对象的方式,避免了重复的构造过程,提高了代码的效率和可维护性。原型模式的核心思想是利用对象的克隆功能来实现新对象的创建,...
这类模式关注对象的创建过程,包括单例模式(Singleton)、工厂模式(Factory)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)和原型模式(Prototype)。它们提供了创建对象的不同策略,使代码更加灵活...
原型模式(Prototype Pattern)是软件设计模式中的一种创建型模式,它提供了一种复制已有对象而不必知道其具体类别的方法。在C++中,原型模式常常利用拷贝构造函数或赋值运算符来实现对象的克隆。这种模式在需要频繁...
原型模式是一种创建型设计模式,它允许用户通过复制现有的实例来创建新的对象,而不是通过传统的构造器来创建对象。这种模式适用于那些创建对象的成本较高,或者当对象的创建依赖于复杂的环境配置时。通过使用原型...
**原型模式(Prototype Pattern)**是一种创建型设计模式,它允许我们通过复制现有的对象来创建新对象,而不是通过构造函数或工厂方法。这种模式的核心在于,它提供了一种更高效、更灵活的方式来创建新实例,特别是在...
本资料包“DesignPattern - DesignPattern.zip”提供了对C++11中23种设计模式的全面讲解,特别是结合指针使用的部分,以下是对这些知识点的详细阐述: 1. **单例模式(Singleton)**:确保一个类只有一个实例,并...
这里我们关注的是一个名为"pattern.zip"的压缩包文件,它包含了23种经典的设计模式,这些模式在实践中被广泛使用,提高了代码的可读性、可维护性和可扩展性。这篇文章将详细探讨这些设计模式及其应用。 首先,23种...
- **原型**(Prototype):用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。 - **单例**(Singleton):确保一个类只有一个实例,并提供一个访问它的全局访问点。 2. **结构型模式**: - **适配器...
在本设计模式课程设计中,我们重点探讨了五个核心的设计模式:原型模式、单例模式、抽象工厂模式、代理模式和建造者模式。这些模式在Java编程中具有广泛的应用,能够帮助开发者创建更加灵活、可扩展和易于维护的代码...
(100%) 设计模式面面观(5):抽象工厂模式(AbstractFactory)-创建型模式 (100%) 设计模式面面观(6):生成器模式(Builder)-创建型模式 (100%) 设计模式面面观(7):原型模式(Prototype)-...
**原型模式(Prototype Pattern)**是一种创建型设计模式,它提供了一种通过复制已有对象来创建新对象的方式,而不是通过构造函数。在某些情况下,当创建新对象的成本非常高时(例如,对象需要大量的初始化操作或者从...
- **原型模式(The Prototype Pattern)**:通过复制已有的实例来创建新对象。 **结构型模式(Structural Patterns)**: - **适配器模式(Adapter)**:将一个类的接口转换成客户希望的另一个接口。 - **装饰模式...