考虑以下Python中的CDK示例(对于这个问题,不需要AWS知识,这应该对基本上任何构建器模式都有效,我只是在这个示例中使用CDK,因为我面临使用这个库的问题。):
from aws_cdk import aws_stepfunctions as step_fn
from aws_cdk import core
app = core.App()
state_machine = step_fn.Chain.start(
step_fn.Pass(app, 'start')
).next(
step_fn.Pass(app, 'foo1')
).next(
step_fn.Pass(app, 'foo2')
).next(
step_fn.Pass(app, 'bar')
)
现在,如果我需要重用构造
.next(
step_fn.Pass(app, 'foo1')
).next(
step_fn.Pass(app, 'foo2')
)
多次,我可以想出这些方法。
- 将代码封装在方法 中
def foo(chain: step_fn.Chain) -> step_fn.Chain:
return chain.next(
step_fn.Pass(app, 'foo1')
).next(
step_fn.Pass(app, 'foo2')
)
# This works but it destroys the readability of the chain as the calling order is inverted.
state_machine = foo(
step_fn.Chain.start(
step_fn.Pass(app, 'start')
)
).next(
step_fn.Pass(app, 'bar')
)
# This is against the builder design to wrap mutability in the builder classes.
state_machine = step_fn.Chain.start(
step_fn.Pass(app, 'start')
)
state_machine = foo(state_machine)
state_machine = state_machine.next(
step_fn.Pass(app, 'bar')
)
- 猴子打补丁
虽然语法看起来很好,但当应用到多人使用存储库的实际项目中时,这似乎容易出错,并且是可维护性的噩梦:
step_fn.Chain.foo = foo
state_machine = step_fn.Chain.start(
step_fn.Pass(app, 'start')
).foo().next(
step_fn.Pass(app, 'bar')
)
我试图看看是否有任何方法实现Python对象的类型类,但找不到任何东西。我找到了dry-python,但不确定它是否可以用于类方法。在Scala中,隐式类可以在不改变任何全局状态的情况下使用流畅的构建器语法。有什么python的方法可以达到同样的效果吗?
编辑:后来发现CDK链支持添加其他链来解决这个特定的问题。一般来说,如果你可以影响构建器的设计,最好添加一个方法extend
等,允许在构建器中添加另一个构建器,这使得在这种情况下易于重用。
你可以用一个类把整个东西包起来:
class ChainHelper:
def __init__(self, app: core.App, chain: step_fn.Chain):
self._app = app
self._chain = chain
这允许您为您的操作提供描述性名称,并允许更多的重用:
class ChainHelper:
def __init__(self, app: core.App, chain: step_fn.Chain):
self._app = app
self._chain = chain
def pass_arg(self, arg: str):
self._chain = self._chain.next(step_fn.Pass(self._app, arg))
return self
def pass_foo(self):
self._chain = self._chain
.next(step_fn.Pass(self._app, 'foo1'))
.next(step_fn.Pass(self._app, 'foo2'))
return self
用法:
state_machine = ChainHelper(app=core.App(), chain=step_fn.Chain)
.pass_arg('start')
.pass_foo()
.pass_arg('bar')