【问题标题】:What are some uses of closures for OOP?OOP 的闭包有哪些用途?
【发布时间】:2011-04-19 11:33:20
【问题描述】:

PHP 和 .Net 有闭包;我一直想知道在 OOP 和设计模式中使用闭包的一些示例,以及它们与纯 OOP 编程相比有哪些优势。

澄清一下,这不是 OOP 与函数式编程,而是如何在 OOP 设计中最好地使用闭包。闭包如何适应工厂或观察者模式?例如,您可以利用哪些技巧来阐明设计并导致更松散的耦合。

【问题讨论】:

标签: language-agnostic oop functional-programming


【解决方案1】:

闭包对于事件处理很有用。这个例子有点做作,但我认为它传达了这个想法:

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

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

我的文件打开器可以直接调用instance.Open(pathToFile)打开文件,也可以由某些事件触发。如果我没有匿名函数 + 闭包,我将不得不编写一个除了响应此事件之外别无其他目的的方法。

【讨论】:

    【解决方案2】:

    任何具有闭包的语言都可以将它们用于蹦床,这是一种将递归重构为迭代的技术。这可以让您摆脱许多算法的幼稚实现遇到的“堆栈溢出”问题。

    蹦床是一个将闭包“反弹”回其调用者的函数。闭包捕获了“剩下的工作”。

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

    通过将递归转换为迭代,您不会爆出堆栈。闭包具有捕获计算状态本身的特性,而不是像递归那样在堆栈上捕获状态。

    【讨论】:

    • 绝招!我想我以前从未见过这种情况。它在 Python 中最常见吗(这可以解释为什么我从未见过这种情况)?
    • 这在 Lisp 世界中很常见。在大多数 Lisp 实现中,它实际上是由编译器自动完成的(“尾调用优化”)。但有时您必须手动完成,因为编译器只能识别特定配置中的模式。
    • 我在命令式语言中不是那个常见的,但是例如LEPL (acooke.org/lepl) 使用蹦床来防止复杂的递归下降解析器溢出堆栈。
    【解决方案3】:

    假设您想为一个类提供创建任意数量的FileOpener 实例的能力,但遵循 IoC 原则,您不希望创建 FileOpeners 的类实际上知道如何这样做(在其他话,你不想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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2010-11-11
      • 2011-04-16
      • 2014-07-29
      • 1970-01-01
      • 2010-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多