关于原型模式的问题

Sil*_*olt 40 design-patterns prototype-pattern

我正在学习不同的设计模式,我有一种强烈的感觉,我错过了理解这种特殊模式的必要部分(或部分).

在我查看的所有网站和GoF书中,我都看到了克隆方法.根据我的理解,当我们需要该对象的不同版本时,我们可以克隆某种类型的对象,但我们不希望使用"new"命令手动创建每个对象(如在Java中).这可以隐藏其具体实现.因此,当我们克隆时,我们可以稍微调整克隆并使其成为我们需要的,而不必知道如何最难创建该对象.这是我的想法吗?

我还被告知,这可以减少子类化,并随后减少您需要进行的类的数量.我不太明白这一部分.有人能帮助我掌握这个吗?

我的最后一个问题是抽象工厂(甚至工厂方法)模式.这些工厂模式和原型模式感觉就像他们试图在创建新对象时隐藏具体实现.什么时候选择其中一个是个好主意?

谢谢你们!

Dev*_*van 60

原型模式

原型导致克隆对象与原始对象不同.在克隆时,原始状态与克隆相同.此后,每个对象可能经历状态改变.您可以将此视为类似的复印原件,然后在几个地方修改复印件.

  • DVD复制:复制主DVD以创建多个副本
  • 报告对象:考虑一个报告对象,其中包含要传递给GUI的已处理信息.原始报告按升序包含数据.现在,使用此模式可以创建类似的报告,但数据按降序排序.

优点

  • 性能:克隆(使用MemberwiseClone)比重新创建新对象(使用new运算符)要便宜得多.请注意,需要覆盖MemberwiseClose()执行深层复制.
  • 对象可以非常动态地克隆,而不需要任何坚持前期实例化.可以在应用程序执行的任何时间创建第一个创建的对象,并且可以在任何时间进行进一步的复制.

什么时候使用它

  • 在运行时指定要实例化的类时,例如,通过动态加载.
  • 当一个类的实例可以只有几个不同的状态组合之一.安装相应数量的原型并克隆它们可能更方便,而不是每次都使用适当的状态手动实例化类.

与工厂模式的比较

原型模式允许对象创建自定义对象,而无需了解其类或如何创建它们的任何细节.所以,正是这个方面看起来很像工厂方法模式.在这两种模式中,客户端可以创建任何派生类对象,而无需了解自己的结构.

但是这两种模式之间的区别在于,Factory Method专注于创建一个非现有对象类型的对象作为fresh creation(通过理解Creator类的确切子类型).该Prototype模式使用类本身,尤其是派生类来执行self duplication操作.


工厂方法模式

在此模式中,客户端(或消费者)向创建者(或工厂)请求类层次结构中的特定类型的对象.工厂类的Creator方法将特定对象的创建委托给派生类,并返回客户端所请求类型的类的对象.实质上,您可以通过单点联系来创建类层次结构的多个对象.

您可以将此视为前往机票柜台(控制器)并通过提供您对机票类型(头等舱,行政人员或经济舱)的偏好来要求机票.用户不关心如何生成票证,即使在对象表示中,第一类和经济票据都是从基本票类派生的.

何时使用

  • 灵活性很重要(低耦合)
  • 对象可以在子类中扩展
  • 有一个特定原因可以选择一个子类而不是另一个子类 - 这个逻辑构成了Factory方法的一部分.
  • 客户端将职责委托给并行层次结构中的子类.


抽象工厂模式

抽象工厂比工厂方法模式更高(更抽象).在这种情况下,人们不仅可以拥有一个单一的工厂,而且可以有多个工厂.它负责创建属于类层次结构的对象而不仅仅是单个类层次结构.

已存在特定的Factory类.但工厂的方法略有不同.每种方法都可以生成一个实例.客户端可以选择适当的方法并获取实例.

如果您以MVC为基础的完美建筑设计示例,客户将成为业务控制器类,而混凝土产品将成为业务实体.工厂是辅助(辅助)控制器.它们与业务控制器的请求相关联.

何时使用

  • 预计该系统将独立于产品的创建方式.它甚至可能期望产品的组成和表现方式独立.术语"产品"适用于客户端开发人员需要通过调用其方法来使用的最终结果对象.
  • 应该可以使用多个产品系列之一配置的系统.因此,系列的实际选择不会在编码时,而是在稍后的配置时.
  • 该系列产品旨在始终如一地工作.
  • 创建是为了产品库.这里更关心的是相关接口而不是实现.


Tom*_*ing 27

你可以通过它的外观得到原型模式.

它如何减少子类

让我们说你正在制作MineCraft并且你正在使用每种不同类型块的原型模式(例如泥土,石头等).所有原型对象实际上都是同一个类Block,但每个对象都设置了不同的属性,因此它的外观和行为不同,例如:

prototypes.dirt = new Block;
prototypes.dirt.texture = new Image("dirt.jpg");
prototypes.dirt.hardness = 1;

prototypes.stone = new Block;
prototypes.stone.texture = new Image("stone.jpg");
prototypes.stone.hardness = 9;
Run Code Online (Sandbox Code Playgroud)

因此,而不是子类化你要写的地方,new DirtBlock或者new StoneBlock你会写prototypes.dirt.clone()prototypes.stone.clone().不需要子类化,但如果需要,您仍然可以选择子类化.

与工厂模式的差异

至于何时选择原型模式而不是工厂模式,有两种情况我可以想到它们的不同之处:

  1. 您可以迭代原型列表,但不能迭代抽象工厂上的所有方法^.继续上面的代码,您可以创建一个随机块,如下所示:

    prototypes.allValues().objectAtIndex(rand() % prototypes.size()).clone();

    如果您使用工厂方法来制作块,则获取随机块将更加困难.

  2. 在创建对象昂贵但复制便宜的情况下,原型模式将更有效.例如,采用此工厂方法:

    Image loadUserImage() { 
        //loads from disk. will be slow
        return new JPEGImage("path/to/user/image.jpg"); 
    }
    
    Run Code Online (Sandbox Code Playgroud)

    如果要重复调用此方法,那么使用原型会更有效:

    Image loadUserImage() {
        //copy in memory. will be fast
        return userImagePrototype.clone();
    }
    
    Run Code Online (Sandbox Code Playgroud)


^这是一个白色谎言,因为你实际上可以根据你正在使用的语言迭代方法,但迭代数组仍然可能是一个更好的解决方案,因为它没有反射/内省复杂.


小智 15

在我看来,使用原型效率的提高是值得怀疑的.不会有效率提升,因为在大多数语言中,clone方法本身会执行对new的调用,以便构造自己的新对象实例.

我在使用原型模式时看到的唯一好处是方便,你知道克隆会给你一个对象的精确副本,这使你不必自己将新对象的属性设置为相同的值并且可能会挣扎深刻的副本.