2010-09-30 45 views
9

PHP和.Net已关闭;我一直想知道在面向对象和设计模式中使用闭包的一些例子,以及它们比纯粹的OOP编程有什么优点。作为一个澄清,这不是一个OOP与函数式编程,而是如何在OOP设计中最好地使用闭包。工厂或观察者模式如何适合封闭?例如,您可以采取哪些技巧来澄清设计并导致松散耦合。关闭OOP有什么用途?

+1

这应该可能是社区wiki。 – notJim 2010-09-30 17:18:32

+0

我想知道同样的事情。这个页面:http://php.net/manual/en/functions.anonymous.php有一个例子,但是它可以很容易地被重写而不用关闭。 – Galen 2010-09-30 17:24:49

回答

3

闭包对事件处理很有用。这个例子是有点做作,但我认为它传达的理念是:

class FileOpener 
{ 
    public FileOpener(OpenFileTrigger trigger) 
    { 
     trigger.FileOpenTriggered += (sender, args) => { this.Open(args.PathToFile); }; 
    } 

    public void Open(string pathToFile) 
    { 
     //… 
    } 
} 

我的文件首战可以通过直接调用instance.Open(pathToFile)打开文件,或者它可以通过一些事件触发。如果我没有匿名函数+闭包,我必须编写一个没有其他目的而不是回应这个事件的方法。

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(); 
    } 
} 
3

有盖可使用他们的蹦床,这是重构递归迭代到的技术的任何语言。这可以帮助你摆脱许多算法难以实现的“堆栈溢出”问题。

一个蹦床是一个函数,它可以将一个闭包“反弹”回到它的调用者。封闭捕捉“其余的工作”。

例如,在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]) 

通过转换递归迭代,你不吹出来的堆栈。闭包具有捕获计算状态的特性,而不是像递归那样捕获堆栈中的状态。

+0

巧妙的把戏!我以前从未见过这种情况。这是最常见的Python(这将解释为什么我从来没有见过这种)? – FrustratedWithFormsDesigner 2010-09-30 18:48:35

+0

这在Lisp世界很常见。在大多数Lisp实现中,它实际上是由编译器自动完成的(“tail-call optimization”)。但是有时你必须手工完成,因为编译器只能识别特定配置中的模式。 – 2010-09-30 18:56:01

+0

我不会*在命令式语言中常见,但例如LEPL(http://www.acooke.org/lepl/)使用trampolining来防止复杂的递归下降解析器溢出堆栈。 – delnan 2010-10-01 09:47:28