2010-09-30 15 views
9

PHP y .Net tienen cierres; Me he estado preguntando cuáles son algunos ejemplos del uso de cierres en OOP y patrones de diseño, y qué ventajas tienen sobre la programación OOP pura.¿Cuáles son algunos usos de los cierres para OOP?

Como aclaración, esto no es un POO en comparación con la programación funcional, sino cómo utilizar mejor los cierres en un diseño OOP. ¿Cómo encajan los cierres, digamos, en las fábricas o en el patrón del observador? Cuáles son algunos trucos que puede utilizar para aclarar el diseño y obtener un acoplamiento más flexible, por ejemplo.

+1

Esto probablemente debería ser wiki comunitario. – notJim

+0

me he preguntado lo mismo. Esta página: http://php.net/manual/en/functions.anonymous.php tiene un ejemplo, pero se puede reescribir fácilmente sin cierres. – Galen

Respuesta

3

Los cierres son útiles para la gestión de eventos. Este ejemplo es un poco artificial, pero creo que transmite la idea:

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

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

mi abridor de archivo se puede abrir un archivo ya sea llamando directamente instance.Open(pathToFile), o puede ser provocada por algún acontecimiento. Si no tuviera funciones anónimas + cierres, tendría que escribir un método que no tuviera otro propósito que responder a este evento.

2

Supongamos que desea proporcionar una clase con la capacidad de crear cualquier número de instancias FileOpener, pero siguiendo los principios de la COI, que no quieren crear la clase FileOpener s saber realmente cómo hacerlo (en otras palabras, no quiero new ellos). En su lugar, desea usar la inyección de dependencia. Sin embargo, solo desea que esta clase pueda generar instancias FileOpener, y no solo cualquier instancia. Esto es lo que puede hacer:

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); 

    } 
} 

Ahora usted tiene su clase que tiene que ser capaz de hacer FileOpener casos. Se puede utilizar la inyección de dependencias para dotarla de esta capacidad, al tiempo que conserva la articulación flexible

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

Cualquier lenguaje que tiene cierres pueden usarlos para cama elástica, la cual es una técnica para la recursividad refactorización en iteración. Esto puede sacarlo de los problemas de "desbordamiento de pila" que las implementaciones ingenuas de muchos algoritmos se topan.

Un trampolín es una función que "hace rebotar" un cierre hacia atrás hasta su llamador. El cierre captura "el resto del trabajo".

Por ejemplo, en Python se puede definir un acumulador recursiva para sumar los valores de una matriz:

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) 

En mi máquina, esta dados a cabo con un desbordamiento de pila cuando la longitud de los elementos excede 998.

la misma función se puede hacer en un estilo cama elástica mediante cierres:

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]) 

mediante la conversión de la recursividad para iteración, no apagar la pila. El cierre tiene la propiedad de capturar el estado del cálculo en sí mismo en lugar de en la pila como lo hace con la recursión.

+0

Buen truco! No creo haber visto esto antes. ¿Es más común con Python (eso explicaría por qué nunca he visto esto)? – FrustratedWithFormsDesigner

+0

Es bastante común en el mundo de Lisp. En la mayoría de las implementaciones de Lisp, el compilador lo hace automáticamente ("optimización de la cola de llamadas").Pero a veces tienes que hacerlo a mano porque el compilador solo puede reconocer el patrón en configuraciones específicas. –

+0

No * que * es común en los idiomas imprescindibles, pero p. LEPL (http://www.acooke.org/lepl/) utiliza trampolín para evitar que los analizadores sintácticos recursivos complejos desborden la pila. – delnan

Cuestiones relacionadas