提问人:Gulzar 提问时间:5/30/2021 最后编辑:Gulzar 更新时间:10/16/2021 访问量:627
“Updater”设计模式,而不是“Builder”
"Updater" design pattern, as opposed to "Builder"
问:
这实际上是与语言无关的,但我总是更喜欢 Python。
构建器设计模式用于在创建对象之前通过委托创建过程来验证配置是否有效。
一些代码需要澄清:
class A():
def __init__(self, m1, m2): # obviously more complex in life
self._m1 = m1
self._m2 = m2
class ABuilder():
def __init__():
self._m1 = None
self._m2 = None
def set_m1(self, m1):
self._m1 = m1
return self
def set_m2(self, m1):
self._m2 = m2
return self
def _validate(self):
# complicated validations
assert self._m1 < 1000
assert self._m1 < self._m2
def build(self):
self._validate()
return A(self._m1, self._m2)
我的问题与此类似,但有一个额外的限制,由于性能限制,我每次都无法重新创建对象。
相反,我只想更新现有对象。
我想出的糟糕的解决方案:
我可以按照这里的建议去做,就像这样使用setters
class A():
...
set_m1(self, m1):
self._m1 = m1
# and so on
但这很糟糕,因为使用 setters
- 胜过封装的目的
- 击败了 buillder(现在的更新程序)的目的,它应该验证在创建或更新后是否保留了一些复杂的配置,在这种情况下是更新。
正如我前面提到的,我无法每次都重新创建对象,因为这很昂贵,而且我只想更新一些字段或子字段,并且仍然验证或子验证。
我可以向它们添加更新和验证方法并调用这些方法,但这违背了委派更新责任的目的,并且在字段数量上很棘手。A
class A():
...
def update1(m1):
pass # complex_logic1
def update2(m2):
pass # complex_logic2
def update12(m1, m2):
pass # complex_logic12
我可以强制更新具有可选参数的方法中的每个字段A
class A():
...
def update("""list of all fields of A"""):
pass
这又是不容易处理的,因为这种方法很快就会成为一种神法,因为有很多可能的组合。
强制方法始终接受 中的更改,并在 中进行验证也行不通,因为需要查看 的内部状态才能进行分解,从而导致循环依赖。A
Updater
Updater
A
如何委托更新对象中的字段A
在某种程度上,
- 不会破坏
A
- 实际上将更新的责任委托给另一个对象
- 随着变得越来越复杂,易于处理
A
我觉得我错过了一些微不足道的东西,无法将构建扩展到更新。
答:
我不确定我是否理解您的所有担忧,但我想尝试回答您的帖子。根据你所写的内容,我假设:
- 验证很复杂,必须检查对象的多个属性,以确定对对象的任何更改是否有效。
- 对象必须始终处于有效状态。不允许对使对象无效的更改。
- 复制对象、进行更改、验证对象,然后在验证失败时拒绝更改的成本太高。
使用方法将验证逻辑移出构建器并移动到单独的类(如 ModelValidator)中validateModel(model)
第一个选项是使用命令模式。
- 创建名为 Python 抽象类/接口的抽象类或接口(我不认为 Python 抽象类/接口,但这很好)。该接口实现了两个方法,以及 .
Update
Update
execute()
undo()
- 具体类的名称类似于 、 或 。
UpdateAdress
UpdatePortfolio
UpdatePaymentInfo
- 每个具体的 Update 对象还包含对模型对象的引用。
- 具体类保存特定类型的更新所需的状态。这些方法存在于类中:
UpdateAddress
UpdateAddress
setStreetNumber(...)
setCity(...)
setPostcode(...)
setCountry(...)
update 对象需要保存属性的当前值和新值。喜欢:
setStreetNumber(aString):
self.oldStreetNumber = model.getStreetNumber
self.newStreetNumber = aString
调用 execute 方法时,将更新模型:
execute:
model.setStreetNumber(newStreetNumber)
model.setCity(newCity)
# Set postcode and country
if not ModelValidator.isValid(model):
self.undo()
raise ValidationError
撤消方法如下所示:
undo:
model.setStreetNumber(oldStreetNumber)
model.setCity(oldCity)
# Set postcode and country
这是很多打字,但它会起作用。通过不同类型的更新可以很好地封装模型对象。您可以通过在更新对象上调用这些方法来执行或撤消更改。您甚至可以存储更新对象列表,以便进行多级撤消和重试。
但是,对于程序员来说,打字需要很多。请考虑使用持久性数据结构。持久数据结构可用于非常快速地复制对象 -- 近似恒定的时间复杂度。这是一个持久数据结构的 python 库。
假设您的数据位于 dict 的持久数据结构版本中。我引用的库将其称为 .PMap
更新类的实现可以更简单。从构造函数开始:
UpdateAddress(pmap)
self.oldPmap = pmap
self.newPmap = pmap
二传手更容易:
setStreetNumber(aString):
self.newPmap = newPmap.set('streetNumber', aString)
Execute 传回模型的新实例,其中包含所有更新。
execute:
if ModelValidator.isValid(newModel):
return newModel;
else:
raise ValidationError
原始对象根本没有改变,这要归功于持久数据结构的魔力。
最好的办法是不要做任何这些。请改用 ORM 或对象数据库。这就是“企业级”解决方案。这些库为您提供了复杂的工具,如事务和对象版本历史记录。
评论
updater
A
IllegalArgumentException
ValueError
A
ABuilder
A