Il mio codice dovrebbe essere DRY o leggibile se non può essere entrambi?


14

Sto scrivendo il codice Ruby per un semplice esercizio di crittografia e mi sono imbattuto frequentemente in questo dilemma (l'esercizio è un codice di solitario se devi sapere). Si tratta di stabilire se dovrei completare la mia logica con variabili descrittive e istruzioni a singolo passaggio che rendono leggibile la funzione anziché istruzioni concise, anche dense, che eliminano la ripetizione e / o minimizzano le opportunità di errori.

Il mio esempio più recente: il mio programma accetta input e, grazie alle rigide linee guida sul formato, può facilmente determinare se l'input deve essere crittografato o decrittografato. Per semplificare, una volta che la chiave di crittografia e il messaggio vengono convertiti / generati per essere compatibili, si tratta di sottrarre la chiave dal messaggio crittografato o aggiungere la chiave a un messaggio non crittografato, per ottenere l'output desiderato (pensare alla chiave come crittografia, messaggio + crittografia = codice; codice - crittografia = messaggio). La posizione DRY mi dice che dovrei convertire il mio messaggio crittografato in modo diverso dal mio messaggio non crittografato in modo che la funzione che accetta la chiave di crittografia e la applica al messaggio non debba mai distinguere. Ho scoperto che questo significa che ho bisogno di alcune istruzioni nidificate nella funzione ma la logica sembra essere solida. Questo codice, tuttavia, non è facilmente leggibile.

D'altra parte, potrei scrivere due diverse funzioni che vengono chiamate in base a un flag impostato quando l'applicazione determina la crittografia o la decrittografia. Questo sarebbe più semplice da leggere ma duplicerebbe la funzione di alto livello dell'applicazione della chiave di crittografia a un messaggio (causando la sua crittografia o decrittografia).

Devo inclinarmi verso un codice leggibile o un codice conciso? O ho perso un altro modo per ottenere questa funzionalità e soddisfare entrambi i principi? È una posizione su una scala in cui si deve considerare lo scopo del progetto e prendere le migliori decisioni per raggiungere tale scopo?

Finora, tendo a enfatizzare il codice DRY conciso sul codice leggibile.


2
Utilizzabile prima, SECCO secondo, Terzo leggibile. Il codice altamente utilizzabile si traduce in un codice di consumo altamente leggibile che soddisfa più facilmente DRY e leggibile. Questo è il motivo per cui vuoi prendere le cattive complessità e confezionarle da qualche parte con una bella API, se non possono essere migliorate; almeno il codice che interagisce con loro sarà risparmiato dall'essere cattivo con questo approccio.
Jimmy Hoffa,

4
alcuni esempi di codice reali possono essere d'aiuto, sospetto che manchi qualche terzo modo come funzioni di ordine superiore per avere codice a secco e leggibile
jk.

2
Non un duplicato. Conciso v. Leggibile e DRY v. Leggibile sono due confronti molto diversi. Non essere ASCIUTTO è molto più pericoloso di non essere conciso.
Djechlin,

Basta non cadere nella trappola che assumere un codice fortemente duplicato è più leggibile perché rientra in uno schema prevedibile. Sospetto che possa essere facile pensare in questo modo, fino a quando non avrai mantenuto un sistema che si duplica così pesantemente da trovare bug sottili in un ramo che non sono nell'altro, rami quasi identici.
KChaloux,

Non capisco cosa intendi per "funzione di alto livello dell'applicazione della chiave di crittografia al messaggio". Se intendi che esiste una sovrapposizione tra encrypt e decrypt, prendi in considerazione l'utilizzo del refactoring del metodo extract per fattorizzare le parti comuni.
Aaron Kurtzhals l'

Risposte:


20

DRY è una linea guida, non una religione. Coloro che lo portano al punto di ASCIUGARE soprattutto, l'hanno portato troppo lontano.

Innanzitutto, il codice utilizzabile è fondamentale. Se il codice non è utile e utilizzabile, non lo è ... e non ha senso scriverlo in primo luogo.

In secondo luogo, un giorno qualcuno dovrà mantenere il tuo codice. Se il tuo codice non è gestibile, interromperanno il tuo "bellissimo" design denso, conciso e DRY maledendo il tuo nome. Non farlo Sono stato quella persona e ogni volta che vedo un certo nome nell'annotazione del codice, rabbrividisco.

Realizzare un codice denso privo di "variabili descrittive" e inserire tutto in espressioni ternarie nidificate con lambda senza alcuna documentazione è intelligente. È bello sapere che puoi farlo, ma non farlo. È molto difficile eseguire il debug del codice intelligente. Evita di scrivere codice intelligente .

La maggior parte del tempo speso in software è dedicato alla manutenzione del software, non alla sua prima scrittura. Scrivi il codice in modo che tu (o qualcun altro) sia in grado di correggere rapidamente e facilmente i bug e aggiungere funzionalità come richiesto con il minor numero possibile di modifiche alla progettazione.


Intuitivamente o no, hai riscontrato un problema che stavo trascurando. Ho scritto il codice "intelligente" il più possibile per questo esercizio, solo per sapere che potevo farlo, che sono d'accordo è bello sapere, ma sono d'accordo con te che è una cattiva pratica. Ora ho molto refactoring da fare.
Lutze,

1
@lutze Sono un programmatore perl e talvolta rubino e posso certamente capire la tentazione di scrivere codice intelligente lì dentro. Una versione pre-post di questa risposta includeva una citazione di Larry Wall su hubris - Credo che questa sia una virtù molto importante quando si lavora con il codice che un giorno verrà mantenuto. A causa della possibile terseness delle lingue, a volte è difficile ottenere il vantaggio di scrivere più codice piuttosto che cercare un codice denso intelligente ... fino a quando non è necessario mantenerlo.

17

Non sono sicuro in base alla domanda che capisci DRY. Il codice DRY non è uguale al conciso. Abbastanza spesso è il contrario.

Qui, non sono sicuro di quale sia il grosso problema per questo esempio. Crea una funzione per crittografare, una funzione per decrittografare, helper per funzionalità comuni (mix byte) e un semplice front-end per accettare input e determinare crittografare / decrittografare ... Non ripetere te stesso.

In generale, tuttavia, esistono sia DRY che la leggibilità per aiutare a mantenere ed estendere il codice. Non tutti gli scenari sono uguali, un grande colpo di leggibilità per rimuovere una piccola ripetizione non è buono, e nemmeno un mucchio di duplicati per aggiungere un po 'di leggibilità.

Se premuto, preferirei la leggibilità. Il codice duplicato può ancora essere testato - codice illeggibile ti porta a fare (e testare) la cosa sbagliata.


Ottimi punti, avevo effettivamente combinato un po 'il principio DRY con l'obiettivo del codice conciso.
Lutze,

12

Non ho familiarità con il tuo caso specifico, ma posso fornire alcune linee guida generali.

Lo scopo sia della leggibilità che del DRY è la manutenibilità .

La manutenibilità è importante nella maggior parte delle situazioni, trascorrerai più tempo a conservare il codice che a scriverlo. Ciò è particolarmente vero se si considera la scrittura del codice come un tipo speciale di manutenzione.

Purtroppo, DRY è spesso frainteso. Questo in parte perché sembra così semplice, ma come molte cose semplici può essere, beh ... complicato.

L'intenzione di DRY è che ogni unità di funzionalità esista in un solo posto. Se viene seguito il principio, il manutentore del codice che ha il compito di modificare o verificare che la funzionalità possa gestire il codice una volta sola. Se non viene seguito DRY, esiste un pericolo molto reale che una copia della funzionalità non venga mantenuta correttamente.

La violazione più evidente di DRY è la codifica copia-incolla, in cui interi blocchi di codice vengono ripetuti testualmente nella base di codice. Questo è sorprendentemente comune nella mia esperienza, e nessun modo aggiunge alla leggibilità. Pertanto, il refactoring del codice per introdurre un metodo comune aumenta invariabilmente la conformità e la leggibilità dei DRY.

La seconda violazione più evidente è "copia-incolla e modificala un po '". Ancora una volta, il refactoriing per introdurre un metodo comune con parametri, o scomporre una funzione in passi e sottrarre i punti in comune aumenta quasi sempre la leggibilità.

Poi ci sono le violazioni più sottili, in cui la funzionalità è duplicata ma il codice è diverso. Questo non è sempre facile da individuare, ma quando lo vedi e rifletti di estrarre il codice comune in un singolo metodo / classe / funzione, il codice è usualmente più leggibile di prima.

Infine, ci sono casi di ripetizione del design. Ad esempio, potresti aver usato più volte il modello di stato nella tua base di codice e stai pensando di eseguire il refactoring per eliminare questa ripetizione. In questo caso, procedere con cautela. Potresti ridurre la leggibilità introducendo più livelli di astrazione. Allo stesso tempo, non hai davvero a che fare con la duplicazione della funzionalità, ma con la duplicazione dell'astrazione. A volte ne vale la pena ... ma spesso non lo è. Il tuo principio guida sarà quello di chiedere "quale di questi è più mantenibile".

Ogni volta che faccio queste chiamate di giudizio provo a considerare la quantità di tempo che le persone impiegheranno per mantenere il codice. Se il codice è la funzionalità aziendale principale, probabilmente avrà bisogno di più manutenzione. In questi casi, ho l'obiettivo di rendere il codice mantenibile per le persone che hanno familiarità con la base di codice e le astrazioni coinvolte. Sarei più felice di introdurre un po 'più di astrazione per ridurre la ripetizione. Al contrario, uno script usato raramente e mantenuto di rado potrebbe essere meno facile da comprendere per i manutentori se comporta troppa astrazione. In tal caso, sbaglierei dalla parte della ripetizione.

Considero anche il livello di esperienza degli altri membri del mio team. Evito astrazioni "fantasiose" con sviluppatori inesperti, ma utilizzo modelli di progettazione riconosciuti dall'industria con gruppi più maturi.

In concusione, la risposta alla tua domanda è fare tutto ciò che rende il tuo codice più gestibile. Che cosa significa nel tuo scenario spetta a te decidere.


Penso che tu abbia risolto esattamente uno dei miei problemi. L'esempio che ho fornito della funzionalità che stavo cercando di non duplicare è più probabilmente la duplicazione dell'astrazione.
Lutze,

+1. Attualmente ho a che fare con un sistema che abusa di copia e incolla in modo ridicolo. Il factoring di un singolo metodo dal codice copia / incollato ha rimosso oltre 400 righe da una delle mie classi oggi. Sfortunatamente, l'ho trovato con un metodo a 900 righe ...
KChaloux,

1
Se due costrutti di codice attualmente fanno la stessa cosa, ma una modifica a uno dovrebbero generalmente non implica un cambiamento all'altro, copia / incolla può essere migliore di factoring funzionalità comuni. Se il codice utilizza due metodi identici carattere per carattere fanno la stessa cosa per scopi diversi e basa costantemente la scelta del metodo sullo scopo richiesto, quindi se le cose in seguito devono essere fatte leggermente per uno di tali scopi, cambiando solo l'appropriato Il metodo influenzerà solo quei comportamenti che dovrebbero essere interessati. L'uso di un unico metodo comune avrebbe potuto rendere la modifica molto più difficile.
supercat

7

Se le tue funzioni diventano più complicate e più lunghe (quindi meno leggibili) quando provi a renderle ASCIUTTE, allora stai sbagliando. Stai cercando di mettere troppe funzionalità in una funzione perché pensi che questo sia l'unico modo per evitare di ripetere gli stessi pezzi di codice in una seconda funzione.

Fare il codice DRY significa quasi sempre refactoring delle funzionalità comuni a funzioni più piccole. Ognuna di quelle funzioni dovrebbe essere più semplice (e quindi più leggibile) di quella originale. Per mantenere tutto nella funzione originale sullo stesso livello di astrazione, ciò può anche significare rifattorizzare parti aggiuntive che non vengono utilizzate altrove. La tua funzione originale diventa quindi una "funzione di alto livello", che chiama quelle più piccole, e diventa anche più piccola e più semplice.

Ciò comporta di conseguenza principalmente livelli diversi di astrazioni nel codice e alcune persone credono che questo tipo di codice sia meno leggibile. Per la mia esperienza, questo è un errore. Il punto chiave qui è dare un buon nome alle funzioni più piccole, introdurre tipi di dati ben noti e astrazioni che possono essere facilmente comprese da una seconda persona. In questo modo "DRY" e "leggibilità" dovrebbero solo molto, molto raramente entrare in conflitto.


2

Anche se non sono sicuro di cosa stia causando il problema qui, la mia esperienza è che quando trovi DRY e leggibilità in conflitto che dice che è il momento di refactificare qualcosa.


0

Sceglierei leggibile (= mantenibile) invece di DRY in qualsiasi giorno della settimana. In realtà entrambi si allineano spesso, qualcuno che ottiene il concetto di DRY generalmente produce codice (principalmente) leggibile.


2
senza una spiegazione, questa risposta potrebbe diventare inutile nel caso in cui qualcun altro pubblichi un'opinione opposta. Ad esempio, se qualcuno pubblica un reclamo del tipo "Vorrei scegliere DRY (= mantenibile) anziché leggibile in qualsiasi giorno della settimana", in che modo questa risposta aiuterebbe il lettore a scegliere due opinioni opposte? Valuta di modificarlo in una forma migliore
moscerino il

0

Molto, molto, molto poche volte nella mia carriera ho trovato un caso legittimo che mette a repentaglio la leggibilità contro DRY. Renderlo prima leggibile. Nomina bene le cose. Copia e incolla se necessario.

Ora fai un passo indietro e guarda la totalità che hai creato, come un artista che fa un passo indietro per guardare il loro dipinto. Ora puoi identificare correttamente la ripetizione.

Il codice ripetuto deve essere rifattorizzato nel proprio metodo o estratto nella propria classe.

La ripetizione del codice dovrebbe essere l'ultima risorsa. Per prima cosa leggibile E ASCIUTTO, non leggibile se si limita l'ambito del codice ripetuto.

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.