——————假设写一款游戏————————-

假设 我们现在要写一款游戏,首先是要先设定角色的基础属性的数值,比如

  1. class Knight(object):
  2. def __init__(self, life, speed, attack_power, attack_range, weapon):
  3. self.life = life
  4. self.speed = speed
  5. self.attack_power = attack_power
  6. self.attack_range = attack_range
  7. self.weapon = weapon
  8. def __str__(self):
  9. return 'Life: {0} \n' \
  10. 'Attack: {1} \n' \
  11. 'Attack Range: {2} \n' \
  12. 'Speed: {3} \n' \
  13. 'Weapon: {4}'.format(
  14. self.life,
  15. self.attack_power,
  16. self.attack_range,
  17. self.speed,
  18. self.weapon
  19. )

此时,我们拥有了第一个基础单位类Knight,接下来是要实现生成Knight,那么我们就需要一个兵营

  1. class Barracks(object):
  2. def generate_knight(self):
  3. return Knight(400, 5, 3, 1, "short sword")
  4. if __name__ == '__main__':
  5. barracks = Barracks()
  6. knight1 = barracks.generate_kngiht()
  7. print("[knight1]", kngiht1)

此时我们就生成了我们第一个knight实例,生命值400,速度5,攻击力3,攻击距离1,武器是短剑。
那么我们现在可以思考这么一点,这个单位如果要升级的话 那么代码是要怎么去写呢?为计划在特定兵营中生成的每一种单位类型使用一个独立函数并非一个好主意。
举个例子,假设我们考虑升级knight类,让他的武器不再是短剑而且长剑,攻击力+5,速度+3,攻击范围扩大+1,那么我们就需要在Barracks类中使用双倍数量的函数,并且需要保留这个类所能生成的兵营和单位的状态的某种记录,以确保生成正确级别的单位
那么为了解决这种困局,可以命名一个名为 generate_unit的方法来替换单独的gennerate_knight,话不多说,直接上代码

我们可以看到,该代码生成了一个级别为2的骑士,并且独立参数符合我们的预期,不需要Barracks类保持追踪每个单位的每个级别以及与之相关的参数。

———————实现原型模式————————

以上只是开胃菜,为了能够让大家更清楚的理解什么时候适合用原型模式来编写代码
例如此时我们有一个需求, 兵营1级时,只能创建骑士level1, 升级至2级兵营时, 骑士可以升级为2级骑士,并且解锁弓箭手。
那么每次一个兵营想要创建一个单位时,它都需要查找它所能创建的单位是哪些,然后发出一条命令来创建所选择的单位。然后单位类就必须要查询相关的参数,之后,在这些参数被传递到要创建的实例的类构造器前,会先读取这些参数,所有这些都是非常低效的。如果一个兵营必须生成类型相同的500个单位,我们就必须对所选的存储系统进行499次重复请求。在用这一数字乘以兵营的数量,然后外加上每个兵营判定其应该有能力生成的单位所需进行的查找。那么目前的做法将马上让系统宕机。
每次都重新加载这些对象的做法并非是可拓展的解决方案,而解决这个问题的办法就是 原型模式

原型模式

在原型模式中,我们优先使用组合而非继承。由各个部分组成的类使得我们可以在运行时替换那些组成部分,从而彻底地改善系统的可测试性和可维护性。要实例化的类是在运行时经由动态加载指定的。原型模式的这一特性的结果就是,子类的应用会显著减少。创建一个新实例的复杂性对客户端是隐藏起来的。所有这一切都很棒,不过这一模式的主要优点在于,它会强制我们面向接口变成,这会带来更好的设计
不过要注意一点,深层克隆的具有循环引用的类必定会引发问题。

现在我们仅希望对我们手头伤的一些对象复制一个副本,要确保该副本是以其应有的方式来设置的,并且为了将该对象的功能从系统其余部分隔离出来,我们打算复制的实例应该提供该复制特性。在该实例上克隆该对象并且之后相应修改其值的clone()方法会是理想的。
原型模式所需的三个组成部分如下:
客户端通过要求原型克隆其资深来创建一个新的对象
原型声明一个用于克隆自己的接口
具体原型实现用于克隆其自身的操作

(浅拷贝与深拷贝的对比在此不再多说)

就其核心而言,原型模式就是一个clone()函数,这个函数接受一个对象做为输入参数并且会返回它的一个副本。
一个原型模式实现的框架应该声明一个抽象基类,这个基类会指定一个纯虚方法clone()。任何需要多态构造函数能力的类都会从该抽象类派生其本身,并且实现这个clone方法。

  1. # Prototype.py
  2. from abc import ABCMeta, abstractmethod
  3. class Prototype(metaclass=ABCMeta):
  4. @abstractmethod
  5. def clone(self):
  6. pass
  1. # rts_prototype.py
  2. from config.linshijiekou import Prototype
  3. from copy import deepcopy
  4. class Knight(Prototype):
  5. def __init__(self, level):
  6. self.unit_type = 'knight'
  7. if level == 1:
  8. self.life = 500
  9. self.attack = 15
  10. self.attack_range = 2
  11. self.speed = 15
  12. self.weapon = 'Sword'
  13. elif level == 2:
  14. self.life = 999
  15. self.attack = 30
  16. self.attack_range = 4
  17. self.speed = 20
  18. self.weapon = " Gold Sword"
  19. def __str__(self):
  20. return 'Type: {0} \n' \
  21. 'Life: {1} \n' \
  22. 'Attack: {2} \n' \
  23. 'Attack Range: {3} \n' \
  24. 'Speed: {4} \n' \
  25. 'Weapon: {5}'.format(self.unit_type,
  26. self.life,
  27. self.attack,
  28. self.attack_range,
  29. self.speed,
  30. self.weapon
  31. )
  32. def clone(self):
  33. return deepcopy(self)
  34. class Archer(Prototype):
  35. def __init__(self, level):
  36. self.unit_type = 'archer'
  37. if level == 1:
  38. self.life = 500
  39. self.attack = 15
  40. self.attack_range = 2
  41. self.speed = 15
  42. self.weapon = 'Short Bow'
  43. elif level == 2:
  44. self.life = 999
  45. self.attack = 30
  46. self.attack_range = 4
  47. self.speed = 20
  48. self.weapon = " Gold Bow"
  49. def __str__(self):
  50. return 'Type: {0} \n' \
  51. 'Life: {1} \n' \
  52. 'Attack: {2} \n' \
  53. 'Attack Range: {3} \n' \
  54. 'Speed: {4} \n' \
  55. 'Weapon: {5}'.format(self.unit_type,
  56. self.life,
  57. self.attack,
  58. self.attack_range,
  59. self.speed,
  60. self.weapon
  61. )
  62. def clone(self):
  63. return deepcopy(self)
  64. class Barracks(object):
  65. def __init__(self):
  66. self.units = {
  67. 'knight': {
  68. 1: Knight(1),
  69. 2: Knight(2)
  70. },
  71. 'archer': {
  72. 1: Archer(1),
  73. 2: Archer(2)
  74. }
  75. }
  76. def build_unit(self, unit_type, level):
  77. return self.units[unit_type][level].clone()
  78. if __name__ == '__main__':
  79. barracks = Barracks()
  80. knight1 = barracks.build_unit('knight', 2)
  81. archer1 = barracks.build_unit('archer', 1)
  82. print("[knight1]", knight1)
  83. print("[archer1]", archer1)

在这些单位类中扩展该抽象基类时,会强制我们实现该clone方法,我们在让兵营生成新的单位时需要使用它。
现在,可以直接克隆具有正确级别的单位,而不必打开一个文件或从外部资源加载任何数据