关于原型模式的问题

我正在学习不同的devise模式,我有一种强烈的感觉,我错过了理解这种特定模式的基本部分(或多个部分)。

在我看过的所有网站和GoF书中,我都看到了克隆方法。 据我所知,当我们需要不同版本的对象时,我们可以克隆一些types的对象,但是我们不需要使用“new”命令(如在Java中)手动创build每个对象。 这可以隐藏其具体实现。 所以当我们克隆的时候,我们可以稍微调整一下克隆,使之成为我们所需要的,而不必知道如何最初创build这个对象。 这是我的想法是否正确?

我也被告知,这可以减less子类,并随后减less你需要做的类的数量。 我不太了解这部分。 有人能帮我把握这个吗?

我最后的问题是关于抽象工厂(甚至工厂方法)的模式。 这些工厂模式和原型模式觉得他们试图在创build新对象时隐藏具体的实现。 什么时候select其中一个是个好主意?

谢谢你们!

原型模式

原型会导致与原始对象不同的克隆对象。 原始状态与克隆时的克隆相同。 此后,每个对象可以经历状态改变。 你可以把这个想法看成是类似的复印原件,然后在几个地方修改复印件。

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

优点

  • 性能:克隆(使用MemberwiseClone )比重新创build一个新的对象(使用new操作符 )要便宜得多。 请注意,需要重写MemberwiseClose()以执行深层复制。
  • 对象可以非常dynamic地进行克隆,而不需要任何前台实例化。 第一个创build的对象可以在应用程序执行中随时创build,并且可以在任何时候进行进一步的复制。

何时使用它

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

与工厂模式的比较

原型模式允许一个对象创build自定义的对象,而不需要知道他们的类或任何细节如何创build它们。 所以,这个方面看起来很像Factory Method模式。 在这两种模式中,客户端可以创build任何派生类对象,而无需了解任何有关它们自己的结构的信息。

但是这两种模式之间的区别在于,对于Factory Method ,通过理解创build者类的确切子types,我们专注于创build一个不存在对象types的对象作为fresh creation对象。 Prototype模式使用类本身,特别是派生类进行self duplication操作。

工厂方法模式

在这种模式下,客户端(或消费者)向创build者(或工厂)请求类层次结构中特定types的对象。 工厂类的Creator方法将特定对象的创build委托给派生类,并返回客户请求的类的类的对象。 从本质上讲,您只需要一个联系人就可以创build一个类层次结构的多个对象。

你可以把这个想象成去一个机票柜台(控制器),并通过给予你的机票types(头等舱,执行或经济)的偏好来要求机票。 用户不关心如何生成票证,即使在对象表示中,第一类和经济票据都是从基础票证类派生的。

何时使用

  • 灵活性很重要(低耦合)
  • 对象可以在子类中扩展
  • 为什么select一个子类会有一个特定的原因 – 这个逻辑构成了工厂方法的一部分。
  • 客户将责任委托给并行层次中的子类。

抽象的工厂模式

抽象工厂比工厂方法模式更高(更抽象)。 在这种情况下,可以有不止一个,而是多个工厂有轻微的变化。 它负责创build属于类层次结构系列的对象,而不仅仅是一个单独的类层次结构。

一个特定的工厂类已经存在。 但工厂的方法稍有不同。 每个方法都可以产生一个实例。 客户端可以select合适的方法并获取实例。

如果以MVC为基础的完美build筑devise示例,客户将成为业务控制员类,而混凝土产品将全部为业务实体。 工厂是辅助(助手)控制器。 它们与业务控制器的请求相关联。

何时使用

  • 预计该系统将独立于产品的创build方式。 它甚至可以期望产品的组成和代表性如何独立。 术语产品适用于客户端开发人员通过调用其方法需要使用的最终结果对象。
  • 该系统应该可以configuration多个产品系列之一。 因此,家庭的实际select将不在编码时间,而是在稍后的configuration时间。
  • 产品系列devise始终在一起工作。
  • 创作是为了一个产品库。 这里更关心的是相关的接口而不是实现。

你已经有了它的样子的原型模式。

它如何减less子类

比方说你正在制作MineCraft,并且你正在使用每种不同types的块的原型模式(例如泥土,石头等)。 所有原型对象实际上都是同一个类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; 

因此,而不是在哪里编写new DirtBlocknew StoneBlock的子类,而是编写prototypes.dirt.clone()prototypes.stone.clone() 。 不需要子类化,但是如果需要的话,你仍然可以select子类化。

与工厂模式的差异

至于何时select原型模式而不是工厂模式,有两种情况我可以想到它们的区别:

  1. 您可以迭代原型列表,但不能遍历抽象工厂中的所有方法。 从上面的代码继续,你可以像这样创build一个随机块:

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

    如果你使用工厂方法来创build块,那么得到一个随机块将会更困难。

  2. 如果创build一个对象是昂贵的,但是复制是便宜的,那么原型将更有效率。 例如,采取这种工厂方法:

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

    如果这个方法将被重复调用,使用如下原型会更有效率:

     Image loadUserImage() { //copy in memory. will be fast return userImagePrototype.clone(); } 

^这是一个白色的谎言,因为你实际上可以迭代方法取决于你使用的是什么语言,但迭代数组仍然可能是一个更好的解决scheme,因为它不如reflection/内省复杂。

在我看来,使用原型的效率的提高是值得怀疑的。 没有效率增益,因为在大多数语言中,克隆方法本身执行一个新的调用,以构build一个新的对象实例。

我在使用原型模式时看到的唯一好处是方便,您知道克隆将会为您提供一个精确的对象副本,使您不必将新对象的属性设置为相同的值,并可能挣扎与深拷贝。

Interesting Posts