原型形式
原型形式是一种创立型规划形式, 使你能够仿制已有目标, 而又无需使代码依靠它们所属的类。
问题
假如你有一个目标, 并期望生成与其完全相同的一个仿制品, 你该怎么完成呢? 首要, 你有必要新建一个属于相同类的目标。 然后, 你有必要遍历原始目标的一切成员变量, 并将成员变量值仿制到新目标中。
不错! 但有个小问题。 并非一切目标都能经过这种办法进行仿制, 由于有些目标可能拥有私有成员变量, 它们在目标本身以外是不可见的。
“从外部” 仿制目标并非总是可行。
直接仿制还有另外一个问题。 由于你有必要知道目标所属的类才干创立仿制品, 所以代码有必要依靠该类。 即便你能够承受额定的依靠性, 那还有另外一个问题: 有时你只知道目标所完成的接口, 而不知道其所属的详细类, 比如可向办法的某个参数传入完成了某个接口的任何目标。
解决方案
原型形式将克隆过程委派给被克隆的实际目标。 形式为一切支撑克隆的目标声明晰一个通用接口, 该接口让你能够克隆目标, 一起又无需将代码和目标所属类耦合。 通常状况下, 这样的接口中仅包括一个 克隆
办法。
一切的类对 克隆
办法的完成都非常类似。 该办法会创立一个当前类的目标, 然后将原始目标一切的成员变量值仿制到新建的类中。 你甚至能够仿制私有成员变量, 由于绝大部分编程言语都允许目标访问其同类目标的私有成员变量。
支撑克隆的目标即为原型。 当你的目标有几十个成员变量和几百种类型时, 对其进行克隆甚至能够替代子类的结构。
预生成原型能够替代子类的结构。
其运作办法如下: 创立一系列不同类型的目标并不同的办法对其进行装备。 假如所需目标与预先装备的目标相同, 那么你只需克隆原型即可, 无需新建一个目标。
实在国际类比
现实生活中, 产品在得到大规模生产前会运用原型进行各种测验。 但在这种状况下, 原型仅仅一种被动的工具, 不参与任何真实的生产活动。
一个细胞的割裂。
由于工业原型并不是真实意义上的自我仿制, 因而细胞有丝割裂 (还记得生物学常识吗?) 或许是更恰当的类比。 有丝割裂会发生一对完全相同的细胞。 原始细胞就是一个原型, 它在仿制体的生成过程中起到了推动作用。
原型形式结构
根本完成
-
原型 (Prototype) 接口将对克隆办法进行声明。 在绝大多数状况下, 其中只会有一个名为
clone
克隆的办法。 - 详细原型 (Concrete Prototype) 类将完成克隆办法。 除了将原始目标的数据仿制到克隆体中之外, 该办法有时还需处理克隆过程中的极端状况, 例如克隆相关目标和整理递归依靠等等。
- 客户端 (Client) 能够仿制完成了原型接口的任何目标。
原型注册表完成
-
原型注册表 (Prototype Registry) 供给了一种访问常用原型的简略办法, 其中存储了一系列可供随时仿制的预生成目标。 最简略的注册表原型是一个
称号 → 原型
的哈希表。 但假如需求运用称号以外的条件进行搜索, 你能够创立更加完善的注册表版别。
伪代码
在本例中, 原型形式能让你生成完全相同的几何目标副本, 一起无需代码与目标所属类耦合。
克隆一系列坐落同一类层次结构中的目标。
一切形状类都遵从同一个供给克隆办法的接口。 在仿制自身成员变量值到结果目标前, 子类可调用其父类的克隆办法。
原型形式适宜运用场景
假如你需求仿制一些目标, 一起又期望代码独立于这些目标所属的详细类, 能够运用原型形式。
这一点考量通常出现在代码需求处理第三方代码经过接口传递过来的目标时。 即便不考虑代码耦合的状况, 你的代码也不能依靠这些目标所属的详细类, 由于你不知道它们的详细信息。
原型形式为客户端代码供给一个通用接口, 客户端代码可经过这一接口与一切完成了克隆的目标进行交互, 它也使得客户端代码与其所克隆的目标详细类独立开来。
假如子类的差异仅在于其目标的初始化办法, 那么你能够运用该形式来削减子类的数量。 他人创立这些子类的目的可能是为了创立特定类型的目标。
在原型形式中, 你能够运用一系列预生成的、 各种类型的目标作为原型。
客户端不用依据需求对子类进行实例化, 只需找到适宜的原型并对其进行克隆即可。
完成办法
-
创立原型接口, 并在其中声明
克隆
办法。 假如你已有类层次结构, 则只需在其一切类中增加该办法即可。 -
原型类有必要另行界说一个以该类目标为参数的结构函数。 结构函数有必要仿制参数目标中的一切成员变量值到新建实体中。 假如你需求修正子类, 则有必要调用父类结构函数, 让父类仿制其私有成员变量值。
假如编程言语不支撑办法重载, 那么你可能需求界说一个特殊办法来仿制目标数据。 在结构函数中进行此类处理比较便利, 由于它在调用
new
运算符后会立刻回来结果目标。 -
克隆办法通常只要一行代码: 运用
new
运算符调用原型版别的结构函数。 留意, 每个类都有必要显式重写克隆办法并运用自身类名调用new
运算符。 不然, 克隆办法可能会生成父类的目标。 -
你还能够创立一个中心化原型注册表, 用于存储常用原型。
你能够新建一个工厂类来完成注册表, 或者在原型基类中增加一个获取原型的静态办法。 该办法有必要能够依据客户端代码设定的条件进行搜索。 搜索条件能够是简略的字符串, 或者是一组复杂的搜索参数。 找到适宜的原型后, 注册表应对原型进行克隆, 并将仿制生成的目标回来给客户端。
最终还要将对子类结构函数的直接调用替换为对原型注册表工厂办法的调用。
原型形式优缺点
-
你能够克隆目标, 而无需与它们所属的详细类相耦合。
-
你能够克隆预生成原型, 避免反复运行初始化代码。
-
你能够更便利地生成复杂目标。
-
你能够用承继以外的办法来处理复杂目标的不同装备。
-
克隆包括循环引用的复杂目标可能会非常麻烦。
代码示例
- Go 原型形式讲解和代码示例 – 掘金 ()