首页 > 其他分享 >第5章 原型模式(Protype Pattern)

第5章 原型模式(Protype Pattern)

时间:2023-06-11 12:33:56浏览次数:30  
标签:colormanager Protype Pattern 原型 ColorPrototype new ConcteteColorPrototype public


原型模式(Prototype Pattern)

——.NET设计模式系列之六

Terrylee,2006年1月

概述

在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化?

意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

结构图

第5章 原型模式(Protype Pattern)_colors

Prototype模式结构图

生活中的例子

Prototype模式使用原型实例指定创建对象的种类。新产品的原型通常是先于全部产品建立的,这样的原型是被动的,并不参与复制它自己。一个细胞的有丝分裂,产生两个同样的细胞,是一个扮演主动角色复制自己原型的例子,这演示了原型模式。一个细胞分裂,产生两个同样基因型的细胞。换句话说,细胞克隆了自己。

第5章 原型模式(Protype Pattern)_colors_02

使用细胞分裂例子的Prototype模式对象图

原型模式解说

我们考虑这样一个场景,假定我们要开发一个调色板,用户单击调色板上任一个方块,将会返回一个对应的颜色的实例,下面我们看看如何通过原型模式来达到系统动态加载具体产品的目的。

很自然,我们利用OO的思想,把每一种颜色作为一个对象,并为他们抽象出一个公用的父类,如下图:

第5章 原型模式(Protype Pattern)_string_03

实现代码:

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");
    }
}

客户程序需要某一种颜色的时候,只需要创建对应的具体类的实例就可以了。但是这样我们并没有达到封装变化点的目的,也许你会说,可以使用工厂方法模式,为每一个具体子类定义一个与其等级平行的工厂类,那么好,看一下实现:

第5章 原型模式(Protype Pattern)_prototype_04


实现代码:

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,这个实例就是一个原型。如下图所示:

第5章 原型模式(Protype Pattern)_colors_05

实现代码:

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很好的满足了依赖倒置原则。

第5章 原型模式(Protype Pattern)_产品_06

通过序列化实现深拷贝

要实现深拷贝,可以通过序列化的方式。抽象类及具体类都必须标注为可序列化的[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与模式》(阎宏 著)


作者: TerryLee

标签:colormanager,Protype,Pattern,原型,ColorPrototype,new,ConcteteColorPrototype,public
From: https://blog.51cto.com/u_130277/6457415

相关文章

  • 第11章 外观模式(Façade Pattern)
    外观模式(FaçadePattern)——.NET设计模式系列之十二Terrylee,2006年3月概述在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化。那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖......
  • 第9章 装饰模式(Decorator Pattern)
    装饰模式(DecoratorPattern)——.NET设计模式系列之十Terrylee,2006年3月概述在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。如......
  • 这款软件轻松实现在线Axure的原型功能
    原型设计是每个产品经理必备的基本技能。本文从即时设计原型设计的步骤开始,帮助您快速使用即时设计制作高还原度、丰富互动的产品原型。 利用即时设计进行原型设计的优势 快速启动原型设计工作 借助即时设计内置设计系统和社区资源,包括大量原型设计模板、自动布......
  • 14) chain of responsibility pattern
    类别: behavioralpattern问题: 高耦合,不灵活if(){}elseif(){}elseif(){}... 方案:    示例: publicclassChainOfResponsibilityPattern{publicstaticvoidmain(String[]args){//灵活装配Pipelinenode1=newNod......
  • 13) Proxy Pattern
    类别: StructuralPattern问题:操纵一个对象时碍手碍脚,与装饰者模式不同之处:装饰者是接口方法,授权小代理则是整个类,授权大方案:   示例: publicclassProxyPatternDemo{publicstaticvoidmain(finalString[]arguments){finalImageimage......
  • 12) Flyweight Pattern
    类别: StructuralPattern问题/动机: 假若绿色是相同部分,占用1M内存,如果提取出来,众对象共享其内容,只占1M内存,否则占10M,且随着对象增多,占用越来越多内存,无疑是浪费资源Aflyweightisanobjectthatminimizesmemoryusagebysharingasmuchdataaspossiblewithot......
  • 11) Facade Pattern
    类别: StructuralPattern问题/动机:系统非常复杂隐藏复杂细节,提供简单界面方案:  示例: /*Complexparts*/publicclassFacadePatternDemo{publicstaticvoidmain(String[]args){CarFacadefacade=newCarFacade();facade.CreateC......
  • cpp: Memento Pattern
     /*****************************************************************//***\fileActorMemento.h*\brief备忘录模式MementoPattern亦称:快照、Snapshot、MementoC++14*2023年6月6日涂聚文GeovinDuVisualStudio2022edit.*\authorgeovindu*\da......
  • compiler expression pattern match
     编译器中经常需要用到patternmatch。那么如何实现呢?比较直观的方法是使用递归。以patternmatch:y=a*(b+c)为例。首先,将其解析成一个抽象语法树:*a+bc其次,递归match:match(y,pattern)=>match(y,'*a+bc')左边是待检测的string,右边的是pattern。只要......
  • 原型模式的用法
    目录一、原型模式的用法1.1介绍1.2结构1.3原型模式类图1.4实现1.4.1克隆的分类1.4.2代码1.5"三好学生"奖状案例1.5.1"三好学生"奖状类图1.5.2代码1.6深、浅克隆的区分1.6.1浅克隆1.6.2深克隆一、原型模式的用法1.1介绍用一个已经创建的实例作为原型,通过复制该原型......