我目前正在写一个python项目,我想用它来学习更多关于软件架构的知识。我已经阅读了一些文本,并观看了几个关于依赖注入的讨论,并学会了如何清楚构造函数注入显示对象的依赖关系。 但是,我有点苦苦于如何将依赖关系传递给对象。我决定不使用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
,它会通过其构造函数传递创建者函数:
# 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
对象的生命周期。作为示例,考虑实现计数器的前缀(有关实现细节,请参阅下面的代码示例)。
我有两个想法如何实现这一点,但是,对我来说,它们似乎都不是完美的:
第一个想法是将构造函数参数添加到Foo
并使其在每个Foo
实例中存储前缀。
明显的缺点是,它混淆了Foo
的责任。它控制业务逻辑并为Bar
提供一个依赖项。一旦Bar
不再需要依赖关系,就必须修改Foo
。对我来说似乎是禁忌。由于我不认为这应该是一个解决方案,我没有在这里发布代码,但为感兴趣的读者提供了on pastebin;)
这种方法不是将Prefix
对象置于Foo
内,而是尝试将其封装在create_foo
函数中。通过为每个Prefix
对象创建一个Foo
并使用lambda
在无名函数中引用它,我将详细信息(也称为is-is-a-prefix-object)远离{{{ 1}}和我的布线逻辑内部。当然命名函数也可以工作(但是lambda更短)。
Foo
这种方法对我来说似乎更有用。但是,我不确定常见的做法,因此担心没有真正建议使用内部函数。来自java / C ++背景,我希望函数依赖于它的参数,它的类成员(如果它是一个方法)或一些全局状态。因此,不使用全局状态的无参数函数每次调用时都必须返回完全相同的值。这不是这种情况。一旦修改了返回的对象(这意味着# 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)
中的counter
已经增加),该函数将返回一个对象,该对象的状态与第一次返回时的状态不同。
这个假设是由我在python中的经验限制造成的吗?我是否必须改变我的思维方式,即不考虑函数而是某些可调用的函数?或者正在向状态提供意外滥用lambda的函数?
为了克服我对有状态函数的疑虑,我可以使用可调用类,其中方法2的prefix
函数将被替换为:
create_foo
虽然这对我来说似乎是一个有用的解决方案,但它更加冗长。
我不完全确定如何处理这种情况。虽然我更喜欢2号,但我还是有疑虑。此外,我仍然希望任何人都能提出更优雅的方式。
请评论,如果有任何您认为太模糊或可能被误解的内容。只要我的能力允许我这样做,我会改进这个问题:) 所有示例都应该在python2.7和python3下运行 - 如果遇到任何问题,请在评论中报告,我会尝试修复我的代码。
答案 0 :(得分:1)
如果你想注入一个可调用的对象,但不希望它有一个复杂的设置 - 如果你的例子中,它实际上只是绑定到一个输入值 - 你可以尝试使用{{3}提供函数<>价值对:
def factory_function(arg):
#processing here
return configurted_object_base_on_arg
class Consumer(object):
def __init__(self, injection):
self._injected = injection
def use_injected_value():
print self._injected()
injectable = functools.partial(factory_function, 'this is the configuration argument')
example = Consumer(injectable)
example.use_injected_value() # should return the result of your factory function and argument
顺便说一句,如果您正在创建一个类似于选项3的依赖注入设置,那么您可能想要了解如何将配置配置到工厂类中,而不是像在此处那样进行内联。这样,如果您想在策略之间进行选择,您可以换掉工厂。它在功能上并没有太大的不同(除非创建比这个例子更复杂并且涉及持久状态)但是如果代码看起来像
那么它会更灵活。factory = FooBarFactory()
bar1 = factory.create_bar()
alt_factory = FooBlahFactory(extra_info)
bar2 = alt_factory.create_bar()