Come abbattere complesso se condizioni che includono l'associazione facoltativa?

5

Recentemente ho letto Clean Code e c'era un bell'esempio in java di quando è utile abbattere una condizione if if complessa in una funzione piuttosto che usare un commento.

// Check to see if the employee is eligible for full benefits
if ((employee.flags && HOURLY_FLAG) && (employee.age > 65))

vs

if (employee.isEligibleForFullBenefits() )

Mi piacerebbe essere in grado di utilizzarlo in modo rapido ma la natura del collegamento opzionale rende difficile questo.

if let employeeFlags = employee.flags,
    let hourlyFlag = self.hourlyFlag,
    employee.age > 65 {
        //Do stuff with unwrapped optionals
    }

Non riesco a raggruppare la condizione in una funzione ben denominata senza forzare lo srotolamento poiché gli optionals che ho scartato non sono più nell'ambito di applicazione. Questo è più bello da leggere, ma se qualcuno dovesse cambiare isEligibleForFullBenefits potremmo bloccarci quando forziamo a scartare un valore nullo.

if employee.isEligibleForFullBenefits {
    //Do something with:
    employee.flags!
    hourlyFlags!
}

Un altro tentativo è stato quello di creare una funzione per la condizione e il blocco di codice dell'istruzione if:

tryToDoSomethingWithEmpolyeeBenefits() o doSomethingWithEmployeeBenefitsIfElligible()

Evito di forzare lo scartare, ma forse sto infrangendo il principio di responsabilità singola dato che i miei controlli di funzione hanno una condizione ed eseguo un'azione basata su quella condizione?

Una di queste è una buona soluzione, ci sono soluzioni migliori o semplicemente non è una buona idea quando si usa swift?

    
posta Declan McKenna 17.03.2018 - 16:28
fonte

1 risposta

2

Ci sono alcuni approcci diversi che potremmo adottare per semplificare il tuo codice di esempio.

Rendili non opzionali (ovvero oggetti nulli e valori predefiniti)

Il modo più diretto per non preoccuparsi di scartare un tipo facoltativo sarebbe di non avere tipi opzionali. Se ci sono impostazioni predefinite sane, potremmo scrivere accessor che restituiranno sempre un valore e non un opzionale. I valori predefiniti sono appropriati per tipi semplici (come booleani, interi, ecc.) In cui il codice sta già assumendo un valore predefinito se l'opzione non è presente e lo stesso valore predefinito ha senso ovunque proviamo a usare il valore. Gli oggetti nulli hanno un ruolo simile per i tipi più complessi. Gli oggetti null implementano la stessa interfaccia dell'oggetto reale ma "non fanno nulla". Gli utenti di accesso possono restituire impostazioni predefinite e i metodi di comando possono avere implementazioni non operative. Se si utilizza un tipo di raccolta (una matrice, un set, un dizionario, ecc.), Una raccolta vuota è un oggetto nullo efficace.

Se non ci sono valori predefiniti sani, non puoi usare questa strategia. Se devi rappresentare oggetti nulli grandi e complessi, mantenere una classe così "non fare nulla" può essere gravoso.

Parametro della funzione Lambda

Invece di un metodo employee.isEligibleForFullBenefits che restituisce un valore booleano, potremmo avere un metodo employee.whenEligibleForFullBenefits che accetta una funzione lambda. La funzione lambda prenderebbe i valori non necessari di cui ha bisogno. Sarebbe chiamato solo se questi valori esistono. Quindi il codice dovrebbe essere:

class Employee {
    func whenEligibleForFullBenefits(
            hourlyFlags optionalHourlyFlags: HourlyFlagsType?,
            thenDo: (EmployeeFlagsType, HourlyFlagsType) -> void) {
        if let employeeFlags = self.flags,
            let hourlyFlags = optionalHourlyFlags,
            self.age > 65 {
            thenDo(employeeFlags, hourlyFlags)
        }
    }
}

Questo ha senso se si tratta di una condizione comune, ma ciò che si fa in base alle condizioni varia ampiamente. Il condizionale è incapsulato e il comportamento è iniettato.

Se invece, questo è solo un condizionale una tantum tra molti, si troveranno rapidamente metodi come questo che proliferano con meno benefici. Questo può essere il caso anche se il condizionale è lo stesso, ma i parametri di input oi valori necessari differiscono. O se questa condizione è comunemente collegata allo stesso comportamento, non vorremmo passarla perché duplicheremo la logica ovunque.

Dillo non chiedere

Il tuo esempio sembra terribilmente interessato ai diversi valori all'interno del dipendente. Invece di interrogare il dipendente per informazioni diverse, dì al dipendente che cosa vuoi che faccia. Questo torna al punto originale che stavano facendo in Clean Code . È anche quello che stai cercando di fare tirando il codice in un metodo doSomethingWithEmployeeBenefitsIfEligible() .

Senza vedere il tuo codice esatto, è difficile dire se questa sia una soluzione ideale. Se ci sono molti qualcosa diversi che potremmo fare, ci ritroveremmo con molti metodi su Employee , il che sarebbe difficile da mantenere.

Rendilo invariato

Sei preoccupato che una modifica al metodo isEligibleForFullBenefits() ti porterebbe a scartare un valore nullo. Si potrebbe dichiarare che il metodo isEligibleForFullBenefits() restituendo true garantisce implicitamente che i valori non siano nulli come parte del suo contratto. Quando qualcuno cambia il metodo, dovrà assicurarsi che gli invarianti continuino a essere validi. Per verificarlo automaticamente, scrivi test automatici approfonditi per verificare le varie combinazioni di stati e che il tuo invariante tiene correttamente. Se qualcuno apporta una modifica che viola i tuoi invarianti, i test falliranno.

"Dichiarare" un invariante spesso non può essere controllato da un compilatore, ma può essere controllato in fase di runtime, tramite test o asserzioni. Ciò significa che c'è un ritardo nel feedback se qualcuno apporta una modifica errata. Potrebbero anche apportare una modifica che non viene verificata dai test automatici, in modo che l'invariante possa essere interrotto involontariamente. Alcuni invarianti non possono essere controllati dal codice, ma possono essere specificati solo nei commenti. La diligenza dello sviluppatore è l'unica tutela quindi.

    
risposta data 18.03.2018 - 22:51
fonte

Leggi altre domande sui tag