PHP和.Net有封闭;我一直想知道在OOP和设计模式中使用闭包的例子是什么,以及它们比纯OOP编程有什么优势。
作为澄清,这不是OOP与函数编程,而是如何在OOP设计中最好地使用闭包。闭包如何适用于工厂或观察者模式?例如,您可以采用哪些技巧来澄清设计并导致更松散的耦合。
答案 0 :(得分:3)
闭包对事件处理很有用。这个例子有点人为,但我认为它传达了这个想法:
class FileOpener
{
public FileOpener(OpenFileTrigger trigger)
{
trigger.FileOpenTriggered += (sender, args) => { this.Open(args.PathToFile); };
}
public void Open(string pathToFile)
{
//…
}
}
我的文件打开器可以通过直接调用instance.Open(pathToFile)
打开文件,也可以通过某些事件触发。如果我没有匿名函数+闭包,我必须编写一个除响应此事件之外没有其他目的的方法。
答案 1 :(得分:3)
任何具有闭包的语言都可以将它们用于trampolining,这是一种将递归重构为迭代的技术。这可以让你摆脱许多算法的天真实现遇到的“堆栈溢出”问题。
蹦床是一种将一个闭包“弹回”到其来电者的功能。关闭捕获“剩下的工作”。
例如,在Python中,您可以定义一个递归累加器来对数组中的值求和:
testdata = range(0, 1000)
def accum(items):
if len(items) == 0:
return 0
elif len(items) == 1:
return items[0]
else:
return items[0] + accum(items[1:])
print "will blow up:", accum(testdata)
在我的机器上,当物品长度超过998时,这会堆叠溢出。
使用闭包可以在蹦床风格中完成相同的功能:
def accum2(items):
bounced = trampoline(items, 0)
while (callable(bounced)):
bounced = bounced()
return bounced
def trampoline(items, initval):
if len(items) == 0:
return initval
else:
return lambda: trampoline(items[1:], initval+items[0])
通过将递归转换为迭代,您不会爆炸堆栈。闭包具有捕获计算状态的属性,而不是像递归一样捕获堆栈。
答案 2 :(得分:2)
假设您想要提供一个能够创建任意数量FileOpener
实例的类,但遵循IoC原则,您不希望创建FileOpener
的类实际知道如何操作所以(换句话说,你不想new
他们)。相反,您希望使用依赖注入。但是,您只希望此类能够生成FileOpener
个实例,而不仅仅是任何实例。这是你可以做的:
class AppSetup
{
private IContainer BuildDiContainer()
{
// assume this builds a dependency injection container and registers the types you want to create
}
public void setup()
{
IContainer container = BuilDiContainer();
// create a function that uses the dependency injection container to create a `FileOpener` instance
Func<FileOpener> getFileOpener = () => { return container.Resolve<FileOpener>(); };
DependsOnFileOpener dofo = new DependsOnFileOpener(getFileOpener);
}
}
现在您的类需要能够创建FileOpener实例。您可以使用依赖注入为其提供此功能,同时保留松散耦合
class DependsOnFileOpener()
{
public DependesOnFileOpener(Func<FileOpener> getFileOpener)
{
// this class can create FileOpener instances any time it wants, without knowing where they come from
FileOpener f = getFileOpener();
}
}