2010-09-30 17 views
9

PHP e .Net hanno chiusure; Mi sono chiesto quali sono alcuni esempi di utilizzo di chiusure in OOP e schemi di progettazione e quali vantaggi hanno rispetto alla pura programmazione OOP.Quali sono alcuni usi delle chiusure per OOP?

Come chiarimento, questa non è una programmazione OOP o funzionale, ma come utilizzare al meglio le chiusure in un design OOP. Come si adattano le chiusure, ad esempio, le fabbriche o il modello di osservatore? Quali sono alcuni trucchi che puoi trarre, ad esempio per chiarire il design e risultati in un accoppiamento più morbido.

+1

Probabilmente questo dovrebbe essere wiki della comunità. – notJim

+0

mi sono chiesto la stessa cosa. Questa pagina: http://php.net/manual/en/functions.anonymous.php ha un esempio, ma può essere facilmente riscritta senza chiusure. – Galen

risposta

3

Le chiusure sono utili per la gestione degli eventi. Questo esempio è un po 'forzato, ma penso che trasmette l'idea:

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

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

mia apertura di file può o aprire un file chiamando direttamente instance.Open(pathToFile), oppure può essere innescato da qualche evento. Se non avessi funzioni anonime + chiusure, dovrei scrivere un metodo che non aveva altro scopo che rispondere a questo evento.

2

si supponga di voler fornire una classe con la possibilità di creare un numero qualsiasi di FileOpener casi, ma seguendo i principi CIO, non si vuole creare la classe FileOpener s sapere realmente come farlo (in altre parole, non voglio new loro). Invece, si desidera utilizzare l'iniezione di dipendenza. Tuttavia, si desidera che questa classe sia in grado di generare istanze FileOpener e non solo qualsiasi istanza. Ecco cosa puoi fare:

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

    } 
} 

Ora hai la tua classe che deve essere in grado di creare istanze di FileOpener. È possibile utilizzare l'iniezione di dipendenza dotarlo di questa funzionalità, pur mantenendo accoppiamento lasco

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

qualunque lingua che comprende chiusure possano usarli trampolino, che è una tecnica per refactoring ricorsione in iterazione. Questo può farti uscire dai problemi di "stack overflow" in cui si imbattono in ingenue implementazioni di molti algoritmi.

Un trampolino è una funzione che "rimbalza" una chiusura indietro al suo chiamante. La chiusura cattura "il resto del lavoro".

Ad esempio, in Python è possibile definire un accumulatore ricorsiva per sommare i valori in una matrice:

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) 

Sulla mia macchina, questa craps con un overflow dello stack quando la lunghezza di elementi supera 998.

la stessa funzione può essere fatto in uno stile trampolino utilizzando chiusure:

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

convertendo la ricorsione per iterazione, non spegnere la pila. La chiusura ha la proprietà di catturare lo stato del calcolo in sé stesso piuttosto che in pila come con la ricorsione.

+0

Trucchi Neat! Non penso di aver mai visto questo prima. È più comune con Python (questo spiegherebbe perché non l'ho mai visto)? – FrustratedWithFormsDesigner

+0

È abbastanza comune nel mondo Lisp. Nella maggior parte delle implementazioni Lisp, in realtà viene eseguito automaticamente dal compilatore ("ottimizzazione tail-call").Ma a volte devi farlo a mano perché il compilatore può riconoscere il modello solo in configurazioni specifiche. –

+0

Non è * quello * comune nelle lingue imperative, ma ad es. LEPL (http://www.acooke.org/lepl/) usa il trampolino per impedire che i parser ricorsivi ricorsivi si estendano dallo stack. – delnan

Problemi correlati