Funziona con effetti collaterali in Delphi / Pascal

4

Qual è l'approccio corretto alle funzioni che hanno effetti collaterali in Delphi / Pascal?

Ad esempio, potrei avere una funzione booleana DeleteFile che restituisce True se il file è stato cancellato e False altrimenti. Come posso raccogliere il valore restituito?

1) La forma molto esplicita sarebbe:

if DeleteFile(MyFilePath) then
begin
  IsMyFileDeleted := True;
end;
else
begin
  IsMyFileDeleted := False;
end;

2) Meno ingombrante sarebbe:

IsMyFileDeleted := False;
if DeleteFile(MyFilePath) then
begin
  IsMyFileDeleted := True;
end;

3) O ancora più breve, ma scoraggiato da JEDI - forse è accettabile solo per raccogliere i valori di ritorno:

IsMyFileDeleted := False;
if DeleteFile(MyFilePath) then IsMyFileDeleted := True;

Il mio problema con 2 e 3 è che dichiaro esplicitamente qualcosa ( X := False ) che forse non so per certo.

4) Preferirei non impostare la variabile in anticipo e avere il suo valore completamente determinato dalla funzione:

IsMyFileDeleted := DeleteFile(MyFilePath);

Qui il problema è che è un po 'fuorviante, perché assegnare un valore a un booleano ha un effetto collaterale piuttosto serio. Non sono sicuro che questo utilizzo sia desiderabile.

Alcuni sostengono che le funzioni non dovrebbero mai avere effetti collaterali, ma penso che alcune funzioni interne di Pascal vi violino.

5) L'alternativa sarebbe rendere le procedure con una variabile risultato da compilare con la procedura:

DeleteFile(MyFilePath, IsMyFileDeleted);

Potrebbe andare in controtendenza per mantenere l'uso dei parametri al minimo (Clean Code).

4 e 5 sarebbero gli unici modi per raccogliere un valore di ritorno diverso da quello booleano, suppongo? Significa qualcosa per le funzioni booleane?

Sì, ci sono molti modi per fare la stessa cosa e forse sono solo preoccupato di nulla, ma sto cercando di sviluppare un buon stile e mi chiedo se ci sono delle buone pratiche. Per un principiante, molti principi sembrano essere in conflitto in ogni momento.

    
posta thoiz_vd 19.09.2011 - 18:33
fonte

5 risposte

10

L'opzione # 4 va bene, davvero. Delphi non è un linguaggio di programmazione funzionale e non prova davvero ad esserlo, quindi parlare di "effetti collaterali" in questo contesto è un po 'sciocco.

L'eliminazione del file non è "un effetto collaterale" del chiamare DeleteFile, è semplicemente l'effetto di chiamarlo. Non lo chiami per ottenere un valore booleano, lo chiami per eliminare un file. Se non altro, l '"effetto collaterale" restituisce un risultato a tutti.

    
risposta data 19.09.2011 - 20:53
fonte
3

Penso che tu stia rendendo le cose troppo difficili per te.

Ci sono molte funzioni nel VCL e nell'API di Windows che funzionano solo perché restituiscono successo o fallimento e solo le funzioni possono restituire qualsiasi cosa. A tutti gli effetti però queste funzioni sono procedure in quello che fanno.

Se non avessero restituito il successo o l'insuccesso come risultato di una funzione, dovresti saltare i telai per verificare se hanno avuto successo, o dover fare affidamento su di loro facendo eccezioni se non erano in grado di fare cosa dovrebbero fare, ma non ha riscontrato problemi reali:

try
  DeleteFile(MyFilePath);
  IsMyFileDeleted := True;
except
  IsMyFileDeleted := False;
end;

Davvero non vuoi sporcare il tuo codice con costrutti del genere. Inoltre, specialmente su win32, le eccezioni sono costose. Inoltre, l'utilizzo di eccezioni per il controllo del flusso non è comunque una buona pratica.

Quindi, usa # 4

IsMyFileDeleted := DeleteFile(MyFilePath);

Fa esattamente ciò di cui hai bisogno ed è abbastanza ovvio che stai solo catturando il successo o il fallimento di DeleteFile.

E se hai una "funzione che è davvero una procedura che segnala il successo o l'insuccesso" attraverso i codici di errore, puoi anche fare qualcosa del tipo:

IsThisSuccessful: = (ProcedureAsFunction = S_OK);

    
risposta data 19.09.2011 - 20:31
fonte
2

In delphi, una delle differenze principali tra le procedure e le funzioni è che una funzione ha sempre un valore di ritorno, e per ogni input, si otterrà un solo valore di ritorno, non importa quante volte si esegue la funzione sullo stesso input.

Quindi, per il tuo codice, # 4 va bene, fino a quando restituisce lo stesso valore su ogni esecuzione dello stesso input . Quindi se si esegue la funzione di eliminazione su un file e si restituisce true, quindi eseguire nuovamente la funzione sullo stesso file, dovrebbe restituire di nuovo true. Anche se il file è già andato! Lo stato corrente del runtime non dovrebbe avere alcun impatto sui valori di ritorno nelle funzioni delphi. A meno che ciò non sia implicito dalla funzione usa stati e / o documentazione . Per un buon esempio, vedi il commento di John qui sotto.

Se lo stato corrente è importante, dovresti utilizzare una procedura che muta una variabile booleana di input, o meglio, un record che può restituire più informazioni, come se il file esiste e se abbiamo il permesso di cancellare.

Oppure, dovresti aggiungere input alla tua funzione, per consentire una maggiore variazione. Un buon sarebbe cosa fare se il file non esiste. Infine, se vuoi semplicemente includere lo stato corrente implicitamente, assicurati di documentarlo.

    
risposta data 19.09.2011 - 21:28
fonte
0

Usa # 4,

IsMyFileDeleted := DeleteFile(MyFilePath);

O forse, dal momento che questo è Delphi,

IsMyFileDeleted := false;
try 
   IsMyFileDeleted := DeleteFile(MyFilePath);
except
   ...
end;

Se vuoi essere paranoico correttamente, se sei preoccupato potresti lanciare un'eccezione. Non penso che tu stia ottenendo qualcosa nelle altre opzioni oltre a rendere inutilmente complicato il codice semplice.

    
risposta data 19.09.2011 - 20:22
fonte
0

Per essere più chiari su cosa sta succedendo, dovresti probabilmente fare qualcosa del genere:

IsMyFileDeleted := False;
if (CanDeleteFile(MyFilePath)) then
begin
    DeleteFile(MyFilePath);
    IsMyFileDeleted := True;
end

La prima riga è vera nel punto in cui è stata eseguita (a meno che il file non esista).

L'uso di CanDeleteFile (...) consente di separare le caratteristiche vero / falso dell'intento del codice dal comportamento eccezionale di non essere in grado di eliminare qualcosa che avrebbe dovuto essere eliminato. Quindi, se puoi cancellare il file, allora DeleteFile dovrebbe sollevare un'eccezione se non può.

A cura

Se scrivessi in C #, la prima situazione sarebbe stata simile a questa. L'utilizzo della gestione delle eccezioni sarebbe probabilmente migliore in Delphi / Pascal, se disponibile. In questo modello, DeleteFile () non restituisce nulla. O funziona, o fornisce informazioni sul motivo per cui non ha funzionato.

try
{
    DeleteFile(MyFilePath);
}
catch (IOException ex)
{
    // Do whatever you would have done if DeleteFile() returned false.
}
    
risposta data 19.09.2011 - 18:59
fonte

Leggi altre domande sui tag