我的情况
我目前正在写一个 python 项目,我想用它来学习更多关于软件架构的知识。我已经阅读了一些文本并观看了一些关于依赖注入的讨论,并学会了喜欢构造函数注入如何清晰地显示对象的依赖关系。但是,我有点挣扎如何将依赖项传递给对象。我决定不使用 DI 框架,因为:
- 我没有足够的 DI 知识来指定我的要求,因此无法选择框架。
- 我想让代码没有更多“神奇”的东西,因为我觉得引入一个很少使用的框架会大大降低可读性。(要阅读的更多代码仅使用了一小部分)。
因此,我使用自定义工厂函数来创建对象并显式传递它们的依赖项:
# Business and Data Objects
class Foo:
def __init__(self,bar):
self.bar = bar
def do_stuff(self):
print(self.bar)
class Bar:
def __init__(self,prefix):
self.prefix = prefix
def __str__(self):
return str(self.prefix)+"Hello"
# Wiring up dependencies
def create_bar():
return Bar("Bar says: ")
def create_foo():
return Foo(create_bar())
# Starting the application
f = create_foo()
f.do_stuff()
或者,如果Foo
必须自己创建多个Bar
s,它会通过其构造函数获取创建者函数:
# Business and Data Objects
class Foo:
def __init__(self,create_bar):
self.create_bar = create_bar
def do_stuff(self,times):
for _ in range(times):
bar = self.create_bar()
print(bar)
class Bar:
def __init__(self,greeting):
self.greeting = greeting
def __str__(self):
return self.greeting
# Wiring up dependencies
def create_bar():
return Bar("Hello World")
def create_foo():
return Foo(create_bar)
# Starting the application
f = create_foo()
f.do_stuff(3)
虽然我很想听听关于代码的改进建议,但这并不是这篇文章的重点。但是,我觉得需要这个介绍才能理解
我的问题
Bar
虽然上面对我来说看起来相当清晰、可读和易于理解,但当每个对象的上下文中要求前缀依赖项相同Foo
并因此与Foo
对象生命周期耦合时,我遇到了一个问题。作为一个例子,考虑一个实现计数器的前缀(有关实现细节,请参见下面的代码示例)。我有两个想法如何实现这一点,但是,它们对我来说似乎都不完美:
1) 通过 Foo 传递前缀
第一个想法是添加一个构造函数参数,Foo
并使其在每个Foo
实例中存储前缀。
明显的缺点是,它混淆了Foo
. 它控制业务逻辑并为Bar
. 一旦Bar
不再需要依赖,Foo
必须修改。对我来说似乎是不行的。因为我真的不认为这应该是一个解决方案,所以我没有在这里发布代码,而是在 pastebin上为非常感兴趣的读者提供了它;)
2) 使用带状态的函数
不是将Prefix
对象放置在Foo
这种方法中,而是尝试将其封装在create_foo
函数中。Prefix
通过为每个对象创建一个Foo
并在无名函数中使用 引用它lambda
,我将细节(又名存在前缀对象)远离Foo
并保留在我的布线逻辑中。当然,命名函数也可以工作(但 lambda 更短)。
# Business and Data Objects
class Foo:
def __init__(self,create_bar):
self.create_bar = create_bar
def do_stuff(self,times):
for _ in range(times):
bar = self.create_bar()
print(bar)
class Bar:
def __init__(self,prefix):
self.prefix = prefix
def __str__(self):
return str(self.prefix)+"Hello"
class Prefix:
def __init__(self,name):
self.name = name
self.count = 0
def __str__(self):
self.count +=1
return self.name+" "+str(self.count)+": "
# Wiring up dependencies
def create_bar(prefix):
return Bar(prefix)
def create_prefix(name):
return Prefix(name)
def create_foo(name):
prefix = create_prefix(name)
return Foo(lambda : create_bar(prefix))
# Starting the application
f1 = create_foo("foo1")
f2 = create_foo("foo2")
f1.do_stuff(3)
f2.do_stuff(2)
f1.do_stuff(2)
这种方法对我来说似乎更有用。但是,我不确定常见的做法,因此担心不建议在函数内部使用状态。来自 java/C++ 背景,我希望一个函数依赖于它的参数、它的类成员(如果它是一个方法)或一些全局状态。因此,不使用全局状态的无参数函数每次调用时都必须返回完全相同的值。这不是这里的情况。一旦返回的对象被修改(这意味着counter
inprefix
已增加),该函数返回一个对象,该对象的状态与第一次返回时的状态不同。这个假设是否只是由于我在 python 方面的有限经验引起的,我是否必须改变我的心态,即不要考虑函数而是考虑可调用的东西?或者为函数提供状态是对 lambda 的无意滥用?
3) 使用可调用类
为了克服我对有状态函数的疑虑,我可以使用可调用类,其中create_foo
方法 2 的函数将被替换为:
class BarCreator:
def __init__(self, prefix):
self.prefix = prefix
def __call__(self):
return create_bar(self.prefix)
def create_foo(name):
return Foo(BarCreator(create_prefix(name)))
虽然这对我来说似乎是一个可用的解决方案,但它太冗长了。
概括
我不确定如何处理这种情况。虽然我更喜欢 2 号,但我仍然有疑问。此外,我仍然希望有人提出更优雅的方式。
请发表评论,如果您认为任何内容过于模糊或可能被误解。我会在我的能力允许的范围内改进这个问题:) 所有示例都应该在 python2.7 和 python3 下运行 - 如果您遇到任何问题,请在评论中报告它们,我会尝试修复我的代码。