原型模式通过复制现有的对象来创建新对象,而不是通过实例化类来创建对象,对此一定感到困惑, 什么样的情况下需要这样做呢,命名实例化类就能创建对象,为什么非要复制?
理解设计模式的一个难点就在于希望能有一个合理的应用场景来帮助理解某一种设计模式的必要性与合理性, 然而几乎大多数的教程都不涉及这些,这导致在学习过程中对设计模式总是存在各种质疑和不解。
原型模式适用场景的特点
原型模式适用的场景有这样的特点:
1.需要大量的创建对象
2.通过实例化类创建对象比较耗时费力
举一个例子,在战争射击类游戏中,在面前会出现大量地方士兵,在程序里,每一个士兵就是一个对象, 为了理解方便,简化士兵对象,一个士兵对象有血量,攻击力,防御力三个属性, 这些属性肯定不会在代码里写死的,而是存储配置文件中,也可能是存储在数据库中, 这样更新游戏的时候,只需要更新配置文件或者数据库就可以随时调整一个士兵的属性。
创建一个士兵对象后,要加载这些属性,不论是从配置文件还是从数据库加载,这都是一个耗时的操作, 当有大量士兵对象需要初始化时,如果都采用实例化类的方法,那么每个对象就都需要加载一次属性, 这样做很耗时,如果能根据已有的士兵对象复制出一个新的对象,那么就不需要执行加载属性的方法了, 这样就可以节省时间。
原型模式的简单示例
原型模式通常包括以下三个角色:
1.原型(Prototype):定义了一个克隆自身的接口,用于复制现有对象。
2.具体原型(Concrete Prototype):实现了原型接口,负责复制自身。
3.客户端(Client):使用原型接口来复制现有对象,并创建新对象。
下面是原型模式的简单示例:
import copy
from abc import ABC, abstractmethod
class Prototype(ABC):
@abstractmethod
def clone(self) -> None:
pass
class SoldierPrototype(Prototype):
def __init__(self):
self.blood = None # 血量
self.attack_power = None # 攻击力
self.defense_power = None # 防御力
def load_config(self):
"""
这里假设根据配置文件加载对象属性是一个耗时的操作
:return:
"""
self.blood = 100
self.attack_power = 90
self.defense_power = 80
def clone(self):
return copy.deepcopy(self)
soldier_prototype = SoldierPrototype()
soldier_prototype.load_config()
soldier_lst = [soldier_prototype.clone() for i in range(100)]
print(soldier_lst[0].blood)
100
程序先创建一个 SoldierPrototype
示例对象,接下来调用 load_config
方法加载对象的属性,
这里必须加载 load_config
方法,它是一个高成本的操作,有了这个原型对象后,
就可以根据它来创建更多的士兵对象,每个士兵对象都是一模一样的,
没必要每个士兵对象都去调用 load_config
方法来加载属性。