Le funzioni di prima classe sostituiscono il modello di strategia?


15

Il modello di progettazione della strategia è spesso considerato un sostituto di funzioni di prim'ordine nelle lingue che le mancano.

Ad esempio, supponiamo che tu voglia passare la funzionalità a un oggetto. In Java dovresti passare nell'oggetto un altro oggetto che incapsula il comportamento desiderato. In un linguaggio come Ruby, passeresti semplicemente la funzionalità stessa sotto forma di una funzione anonima.

Comunque ci stavo pensando e ho deciso che forse la strategia offre più di una semplice funzione anonima.

Questo perché un oggetto può contenere uno stato esistente indipendentemente dal periodo di esecuzione del metodo. Tuttavia, una funzione anonima da sola può contenere uno stato che cessa di esistere nel momento in cui la funzione termina l'esecuzione.

In un linguaggio orientato agli oggetti che supporta funzioni di prima classe, il modello di strategia ha qualche vantaggio rispetto all'utilizzo delle funzioni?


10
"Tuttavia, una funzione anonima da sola può mantenere uno stato che cessa di esistere nel momento in cui la funzione termina l'esecuzione.": Questo non è vero: una chiusura può contenere uno stato che vive attraverso invocazioni diverse.
Giorgio,

"Tuttavia, una funzione anonima di per sé può contenere solo uno stato che cessa di esistere nel momento in cui la funzione termina l'esecuzione." : senza dimenticare le variabili globali e almeno le variabili statiche.
gbjbaanb,

Risposte:


13

Quando il linguaggio supporta i riferimenti alle funzioni ( Java fa dalla versione 8 ), queste sono spesso una buona alternativa alle strategie, perché di solito esprimono la stessa cosa con meno sintassi. Tuttavia, ci sono alcuni casi in cui un oggetto reale può essere utile.

Un'interfaccia strategica può avere più metodi. Prendiamo un'interfaccia RouteFindingStragegycome esempio, che incapsula diversi algoritmi di ricerca del percorso. Potrebbe dichiarare metodi simili

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

che quindi sarebbero tutti implementati dalla strategia. Alcuni algoritmi di ricerca del percorso potrebbero consentire ottimizzazioni interne per alcuni di questi casi d'uso e altri no, quindi l'implementatore può decidere come implementare ciascuno di questi metodi.

Un altro caso è quando la strategia ha uno stato interiore. Certo, in alcune lingue le chiusure possono avere uno stato interiore, ma quando questo stato interiore diventa molto complesso, spesso diventa più elegante promuovere la chiusura a una classe a tutti gli effetti.


2
"quando questo stato interiore diventa molto complesso, spesso diventa utile promuovere la chiusura a una classe a pieno titolo": Perché? Se lo stato interno diventa complesso, puoi anche inserirlo in un oggetto / record memorizzato all'interno della chiusura.
Giorgio,

1
@Giorgio Ma poi avresti due entità di sintassi da mantenere: la chiusura e la classe che gestisce il suo stato interno. Quindi il codice potrebbe essere semplificato spostando la chiusura in quella classe. Questo potrebbe essere migliore oppure no, a seconda dell'esatto caso d'uso, del linguaggio di programmazione e delle preferenze personali.
Philipp,

Mi sembra una visione ragionevole.
Giorgio,

5

Non è vero che una funzione anonima può mantenere lo stato che cessa di esistere solo quando la funzione termina l'esecuzione.

Prendi il seguente esempio in Common Lisp:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

Questa funzione accetta un elenco di stringhe e antepone un contatore a ciascun elemento dell'elenco. Quindi, per esempio, invocando

(number-strings '("a" "b" "c"))

("1: a" "2: b" "3: c")

La funzione number-stringsutilizza internamente una funzione anonima con una variabile counterche mantiene lo stato (il valore corrente del contatore) che viene riutilizzato ogni volta che viene invocata la funzione.

In generale, puoi pensare a una chiusura come a un oggetto con un solo metodo. In alternativa, un oggetto è una raccolta di chiusure che condividono le stesse variabili chiuse. Quindi non sono sicuro se ci sono casi in cui è necessario utilizzare un oggetto anziché una chiusura: direi che entrambi sono modi di vedere lo stesso schema da diverse prospettive.

In particolare, il modello di strategia richiede un oggetto con un solo metodo, quindi una chiusura dovrebbe fare il lavoro. Ma, come ha osservato Philipp nella sua risposta, a seconda delle circostanze (stato complesso) e dei linguaggi di programmazione, è possibile ottenere una soluzione più elegante utilizzando gli oggetti.


Quindi, in un linguaggio che supporta funzioni di prima classe come chiusure, useresti mai la strategia "classica"?
Aviv Cohn,

1
Tendo ad essere d'accordo con Philipp: dipende dalla lingua e dalle preferenze personali. Sceglierei sempre l'approccio che rende la notazione il più semplice possibile. Ad esempio, in Lisp ho potuto definire il mio stato come un elenco di variabili tramite a lete quindi definire la mia chiusura al suo interno. Fondamentalmente, avrei definito un oggetto con un metodo al volo. In un'altra lingua (es. Java) potrebbe essere più conveniente (sintatticamente più semplice) definire un oggetto adeguato per contenere lo stato. Quindi, deciderei caso per caso.
Giorgio,

1

Solo perché due progetti possono risolvere lo stesso problema non significa che si tratti di sostituzioni dirette reciproche.

Se è necessario tenere traccia dello stato in un programma funzionale, non è necessario mutare una variabile chiusa, anche se la lingua lo consente. Si organizza di chiamare una funzione che accetta uno stato come argomento e restituisce il nuovo stato come valore restituito.

La tua architettura avrà un aspetto molto diverso, ma raggiungerai lo stesso obiettivo. Non cercare di forzare i modelli di un paradigma direttamente sull'altro.


"Se devi tenere traccia dello stato in un programma funzionale, non devi mutare una variabile chiusa, anche se la lingua lo consente.": Sono un fan di stile puramente funzionale e sono d'accordo con i tuoi consigli. D'altra parte, le chiusure non sono solo un costrutto funzionale, e tanto meno puramente funzionale. L'idea di chiudere le variabili dal contesto lessicale è ortogonale alla trasparenza / immutabilità referenziale.
Giorgio,

Scusa, ma non posso seguire le tue argomentazioni. Cosa c'entra la gestione dello stato nella programmazione puramente funzionale con la domanda a portata di mano?
Philipp,

1
Il punto è che se usi una parte di un paradigma funzionale, come le funzioni di prima classe, non stupirti se devi inserire altre parti del paradigma per farlo funzionare senza problemi.
Karl Bielefeldt,

1

La strategia è un concetto , una ricetta utile per risolvere un problema particolare e ricorrente. Non è un costrutto di linguaggio, né si tratta di alcuna forma di implementazione . Una chiusura può essere utilizzata per implementare la strategia un giorno e Observer il giorno successivo.

Il termine Strategia è utile soprattutto nelle conversazioni con altri programmatori per esprimere concisamente le tue intenzioni. Non c'è niente di magico in questo.


2
La domanda menziona specificamente il modello di progettazione della strategia che ha una struttura di classe specifica. L'altro significato di "strategia" come piano d'azione inteso a raggiungere un obiettivo specifico non è preciso in questo contesto.

Sono propenso a concordare con @Snowman. Sei sicuro di parlare del modello di strategia ?
Rowan Freeman,

1
@Snowman, anche la pagina a cui ti sei collegato non indica esattamente come dovrebbe essere implementato questo modello, piuttosto forniscono esempi in linguaggi specifici, ma non c'è nulla nel diagramma UML che dice che devo usare l'ereditarietà C ++, le interfacce Java o i blocchi Ruby . Quindi non sono d'accordo con la tua analisi.
idoby,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.