I peggiori anti-pattern che hai incontrato [chiuso]


9

Quali sono i peggiori anti-schemi che hai incontrato nella tua carriera di programmatore?

Sono principalmente coinvolto in Java, anche se probabilmente è indipendente dalla lingua.

Penso che il peggio sia quello che io chiamo il principale anti-schema . Significa programma costituito da una singola classe estremamente grande (a volte accompagnata da una coppia di piccole classi) che contiene tutta la logica. In genere con un grande ciclo in cui è contenuta tutta la logica aziendale, a volte con decine di migliaia di righe di codice.

Risposte:


38

Codice commentato. Blocchi di esso, forse centinaia di linee. Essendo la teoria, ehi, è commentata, non fa alcun danno e forse ne avremo bisogno in futuro.


7
Beh, almeno puoi tranquillamente cancellarlo. A differenza di qualcuno con cui ho lavorato che spesso rinominava le procedure e lasciava accessibile il codice morto. Blech.
Jay,

@Cyrena, hai lavorato anche con Eric?!?
CaffGeek,

Una volta avevo un sacco di codice che utilizzava #ifdef COMMENT per bloccare le cose. Ha funzionato alla grande fino a quando qualcuno non ha definito COMMENTO.
Michael Kohne,

9
Questo è un altro caso in cui l'utilizzo del controllo versione ripaga. Puoi eliminare il codice senza dover chiedere se in futuro potresti averne bisogno o meno perché può essere recuperato di nuovo con pochi tasti.
Jason B,

5
Penso che il codice commentato abbia un posto, se lo si prefigge con un commento che spiega perché è lì. A volte lascerò un blocco commentato che rappresenta una potenziale strada che stavo pensando di prendere, e lo lascerò con un commento sul perché non l'ho fatto. @Jason, ho sicuramente sentito che questo è il ruolo del controllo della versione, ma il codice eliminato nel controllo della versione non è molto rilevabile.
nlawalker,

19

Ne colpirò un altro ovvio con "copia-pasta". Copia di codice quasi identico a quello che desideri, quindi modifica alcune cose (invece di estrarre in un metodo).

Ciò è particolarmente diffuso in alcuni codici di test funzionali e API della fine degli anni '90: letteralmente centinaia (o addirittura migliaia) di casi di test quasi identici che avrebbero potuto essere suddivisi in alcune funzioni che accettano 3 o 4 parametri - o, ancora meglio , qualcosa guidato dai dati. Il mio primo lavoro fuori dal college è stato letteralmente 8 mesi di riscrittura e refactoring di migliaia di righe di pasta per copia che utilizzavano metodi obsoleti. Quando ho finito, i file di test erano meno di un decimo della loro dimensione originale e molto più gestibili (e leggibili!).


16

Penso di poter scrivere molto su Pattern Mania e sulle soluzioni che potrebbero essere risolte con molto meno sforzo, ma preferirei indicare un ottimo articolo che ho letto di recente con un fantastico esempio di come una soluzione semplice può essere complicata .

Come (non) scrivere Factorial in Java o giù di lì inseriamo una factory nel tuo algoritmo


3
Eccezionale! Dov'è il FactorialWebService? : P
FrustratedWithFormsDesigner

1
Lo chiamo Pattern Fever ... tutti lo capiscono ad un certo punto della loro carriera. Il meglio tra noi cresce oltre. Ho avuto un'intervista in cui il ragazzo mi ha chiesto quando dovrei pensare ai modelli. Gli ho detto che ho lasciato che si evolvessero nel sistema. Ha detto "No, dovresti usare i modelli dall'inizio."
Michael Brown,

FactoryFactories è solo un sintomo di voler rinviare il più possibile la scelta effettiva, ma finisce comunque per codificarla o mappare un valore di stringa esterna su un pezzo di codice. L'iniezione di dipendenza è una soluzione migliore.

I motivi sono artefatti di buon design: devono essere trattati come tali. Mi piace descriverli come definizioni piuttosto che soluzioni. Sono utili durante la comunicazione, ma non necessariamente nella progettazione.
Michael K,

Grazie per quello, buona lettura.
Syg,


14

Regioni

In C # puoi definire una regione di codice che può essere compressa nell'IDE, nascondendola così a meno che tu non voglia gestire quel codice. Ho partecipato (sono attualmente attivo) a un progetto in cui le regioni si estendevano su centinaia di linee (vorrei essere esagerata) e c'erano più regioni in una funzione da mille linee (ancora una volta vorrei scherzare).

Sul lato positivo, lo sviluppatore che ha creato la regione ha fatto un ottimo lavoro nell'identificare funzioni specifiche all'interno di una regione. Tanto che sono stato in grado di fare un metodo di estrazione sulla regione e andare avanti.

Le regioni incoraggiano gli sviluppatori a "nascondere" la loro merda in bella vista.


15
+1 per le Regioni incoraggia gli sviluppatori a "nascondere" le loro schifezze in bella vista. Tuttavia, se usato correttamente, trovo che le regioni aiutino a raggruppare logicamente insieme il codice e trovarlo facilmente in un secondo momento.
Darren Young,

Questo è stato a lungo un problema con gli editor pieghevoli. Facevo lo stesso genere di cose quando ho programmato l'ultima volta in OCCAM.
Michael Kohne,

2
Adoro le regioni ... ma solo per l'organizzazione ... non nascondendo risme digitali di codice.
Estratto

3
+1. Questo è anche il motivo per cui l'utilizzo delle regioni viola la regola StyleCop SA1124 DoNotUseRegions.
Arseni Mourzenko,

1
Ho progetti che funzionano con un numero enorme di campi in SQL e il codice che lo aggiorna / crea è di molte righe. Una regione la #region SQL Updaterende pieghevole, quindi è necessario meno scorrimento.
JYelton,


9

Metodi non di sovraccarico:

// Do something
public int doSomething(Data d);

// Do same thing, but with some other data
public int doSomething2(SomeOtherData d);

Indica chiaramente che il programmatore non ha capito il sovraccarico.



5

Soft coding , ovvero quando i programmatori fanno di tutto per evitare l'hard-coding e finiscono dall'altra parte della scala - dipendenze "hard coded".


4

Mantieni stato nel client.

Una volta funzionava con un'applicazione Web in cui tutto lo stato veniva mantenuto nel browser. (Per garantire una scalabilità senza problemi, tutte le eccezioni sono state ignorate).


Potresti per favore elaborare? Imho, per un'applicazione Web, va bene quando l'unico stato è nel browser (cioè i cookie) e il server è "condiviso nulla". O intendi "stato" come "database delle applicazioni"?
keppla,

Stato: come nei dati effettivi su cui operare.

OO hai le mie più profonde simpatie.
keppla,

4

Massicci check-in

Odio quando vedo che uno sviluppatore non ha effettuato il check-in da oltre una settimana. Significa che o è bloccato e non ha cercato aiuto, o sta raggruppando un sacco di funzioni in un unico grande check-in. (Ho lasciato fuori lo scenario peggiore, semplicemente non sta facendo nulla. È facile da risolvere ... due parole sembrano come se fossi assunto.)

Se effettui un check-in di grandi dimensioni, perdi molti vantaggi di SCM, come la possibilità di collegare un changeset a una funzionalità specifica. Inoltre, è probabile che ci sia molta fusione da fare e non è necessariamente facile da ottenere.


1
Non fare nulla non è sempre lo scenario peggiore. A volte è meglio scriverlo da zero piuttosto che avere un codice da riscrivere ...
Malachi,

4

Attualmente sto lavorando con un pezzo di codice legacy e adoro il modo in cui il programmatore precedente ottiene il primo elemento di un elenco:

String result;
for(int i = 0; i < someList.size(); i++) {
    result = someList.get(i);
    break;
}

Ma la cosa peggiore che ho visto in questo codice è la definizione delle pagine di JSP incorporate nelle classi, scrivere tutto il codice HTML, CSS e Javascript usando scriptlet e out.println :-(


4

Uno dei peggiori schemi anti-comportamentali che ho visto è stato un negozio che ha permesso al codice di essere controllato per il controllo della versione solo dopo che era in produzione. Insieme a checkout esclusivi in ​​VSS, ciò ha portato a un processo contorto di annullamento dei checkout se un difetto di produzione doveva essere corretto prima della prossima versione, per non parlare di più di una persona che aveva bisogno di cambiare un determinato file per la prossima versione.

Il fatto che si trattasse di una politica dipartimentale lo rendeva persino peggiore del caso in cui un singolo sviluppatore si comportasse in questo modo.


3

Bloccare letteralmente una stringa

synchronized("one") { /* block one A*/ }

synchronized("one") { /* block one B*/ }

Nomi di classe molto lunghi. (nel JRE)

com.sun.java.swing.plaf.nimbus.
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

Scarsa struttura ereditaria

com.sun.corba.se.internal.Interceptors.PIORB extends
com.sun.corba.se.internal.POA.POAORB extends
com.sun.corba.se.internal.iiop.ORB extends
com.sun.corba.se.impl.orb.ORBImpl extends
com.sun.corba.se.spi.orb.ORB extends
com.sun.corba.se.org.omg.CORBA.ORB extends 
org.omg.CORBA_2_3.ORB extends
org.omg.CORBA.ORB

Un'eccezione che non lo è.

public interface FlavorException { }

Gestione degli errori inutile e criptica

if (properties.size() > 10000)
   System.exit(0);

Creazione non necessaria di oggetti

Class clazz = new Integer(0).getClass();
int num = new Integer(text).intValue();

Generare un'eccezione per altri scopi

try {
    Integer i = null;
    Integer j = i.intValue();
} catch (NullPointerException e) {
    System.out.println("Entering "+e.getStackTrace()[0]);
}

Utilizzo di oggetti di istanza per metodi statici.

Thread.currentThread().sleep(100);

Sincronizzazione su un campo non finale

synchronized(list) {
   list = new ArrayList();
}

Copia inutile di una stringa costante

String s = new String("Hello world");

Chiamata inutile a String.toString ()

String s = "Hello";
String t = s.toString() + " World";

Chiamate a System.gc () per liberare memoria

Impostazione della variabile locale su null per liberare memoria

    // list is out of scope anyway.
    list = null;
}

Utilizzo di ++ i anziché i ++ per motivi di prestazioni (o qualsiasi altra micro-micro-ottimizzazione)


Questi non sono necessariamente anti-schemi, solo cattiva codifica.
Gary Willoughby,

@Gary, o modelli di sviluppo poveri che vedo ripetuti. Forse non all'interno della definizione rigorosa di un anti-pattern.
Peter Lawrey,

1
@Peter: "Chiamate a System.gc () per liberare un po 'di memoria", ho visto un'occasione in cui .NET sarebbe esploso con l'eccezione OutOfMemory a meno che GC non fosse chiamato esplicitamente. Ovviamente era più un'eccezione che una regola.
Coder

3

Codice cosparso di:

// TODO: 

o

// TODO: implement feature 

senza ulteriori informazioni.


2

Iteratore per manichini:

Iterator iCollection = collection.iterator();
for(int i = 0 ; i < collection.size() ; i++ ){
    if(collection.get(i) == something){
       iCollection.remove();
    }
 }

Singletono-Factory:

public class SomeObject{
   private SomeObject() {}
   public static SomeObject getInstance(){
       return new SomeObject();
   }
}

sviluppo guidato da if-else (chiamato anche principio di chiusura aperta - aperto per modifica chiudi per comprensione):

if (sth1){
...  
}else if(sth2){
..
}
...
..
else if(sth1000000000000){
...
}

StringPattern (chiamato anche StringObject):

a) sendercode
if(sth1)
   str+="#a";
if(sth2)
   str+="#b";
...
if(sth1000)
   str+="#n";

b) receiver
   regexp testing if str contains #a, #b, ... #n

else ifTuttavia, questo è spesso l'unico modo per fare le cose. Sto pensando alle stringhe; non è possibile utilizzare un interruttore. Le condizioni dovrebbero essere uguali per essere utili.
Michael K,

IMHO ciascuno se / else può essere sostituito con una semplice mappatura o modello di visitatore. In caso di stringhe, ad esempio, potresti avere un file delle proprietà come: someString1 = some.package.ObjectToHandleSomeString1
Marcin Michalski,

2
Il singleton è una fabbrica . Non c'è niente di sbagliato in quel codice. L'unica cosa che potrebbe essere sbagliata è che il codice esterno presuppone che ogni chiamata a getInstancerestituisca la stessa istanza. Un simile presupposto spezzerebbe l'incapsulamento ed è in effetti uno degli anti-schemi più comuni.
back2dos,

questo è esattamente il motivo per cui è così confuso :) se il metodo fosse chiamato create () o restituirebbe la stessa istanza ogni volta che tutto andrebbe perfettamente bene
Marcin Michalski,

2

Non è tanto un modello di codifica, ma un modello comportamentale, ma è piuttosto male: modificare qualcosa nel codice (diciamo che i requisiti sono cambiati), quindi modificare tutti i test unitari fino a quando il codice non lo supera. Ritoccare o semplicemente rimuovere tutto il codice di test dal metodo di test, ma lasciando lì il metodo.

Questo è collegato a uno più generico, il modello That'll Do , ecco una riga di codice rappresentativa per questo:

int num = new Integer( stringParam ).parseInt( stringParam );

Funziona, dopo tutto.


2

Disprezzo assolutamente l'inversione dell'astrazione o il reinventare primitivi di basso livello in aggiunta a primitivi di alto livello. A volte, tuttavia, ciò è causato da progettisti che parlano male, non da programmatori cattivi. Esempi:

  1. Utilizzando una singola classe di metodo senza variabili membro e un'interfaccia corrispondente (implementata in termini di tabelle di puntatori a funzione), anziché un puntatore a funzione. Nota che in lingue come Java, potresti non avere scelta.

  2. In MATLAB e R, l'insistenza sul fatto che tutto sia un vettore / matrice piuttosto che una primitiva.

  3. Mentre rubiamo MATLAB, che ne dici del fatto che non ha numeri interi, quindi quando hai bisogno di un numero intero devi usare un doppio.

  4. Linguaggi puramente funzionali in cui è necessario utilizzare le chiamate di funzione per scrivere un ciclo.


1

Sarebbe sicuramente copia / incolla, ho visto fare un sacco di codice cattivo a causa di ciò, quello e la codifica da cowboy e tutto ciò che ne deriva. (God Classes, metodi extra large, algoritmi pensati male, ecc.)

E se i modelli di progettazione sono consentiti, li direi: fare un progetto come un insieme di azioni da un piano, senza fare alcuna analisi di progettazione o dominio.


1

Fava java multiuso -

Un bean java con molte variabili, utilizzato in diversi tipi di operazioni. Ogni operazione utilizza un sottoinsieme arbitrario delle variabili bean e ignora le altre. Alcune variabili per lo stato della GUI, alcune variabili inserite per il passaggio tra i componenti, alcune che probabilmente non sono più utilizzate. I migliori esempi non hanno documentazione, il che ostacolerebbe l'apprezzamento del modello.

Inoltre, non posso dimenticare l'amato

try{ 
   ... //many lines of likely dangerous operations
}
catch(Exception e){}

IMHO, le eccezioni puzzano. Sono troppo difficili per farli funzionare bene e aggiungono un falso senso di sicurezza.
Coder

Qualunque sia la tua opinione sulla puzza delle eccezioni, deglutire in silenzio uno deve puzzare di più.
Steve B.

1

Un ex collaboratore aveva l'abitudine di riutilizzare gli oggetti sovrascrivendone le proprietà, anziché semplicemente crearne di nuovi. Non avrei mai immaginato la quantità di problemi che ciò ha causato durante l'implementazione di nuove funzionalità.


1

Qualcosa che mi ha causato molto dolore è il modello "Grande mappa nel cielo". Lanciare attorno a una mappa invece di usare oggetti adeguati. Non hai idea di quali "variabili" contenga senza il debug e non sai cosa potrebbe contenere senza risalire al codice. Tipicamente mappa Stringhe su Oggetti o Stringhe su Stringhe, che potenzialmente dovresti analizzare in primitive.


Sembra un po 'come fare affidamento su Session e ViewState in ASP.NET per passare gobs di dati tra le pagine, che purtroppo è spesso richiesto ...
Wayne Molina,

1

Uno dei miei schemi anti sviluppo preferiti è l'uso di un "design" di database che richiede l' aggiunta continua di colonne aggiuntive a una o più tabelle a livello di programmazione . Questo è un cugino del "design" che crea una nuova tabella per ogni istanza di un'entità. Entrambi inevitabilmente colpiscono i limiti del server di database, ma di solito non fino a quando il sistema è in produzione da un po 'di tempo.


0

Penso che uno dei peggiori anti-pattern che abbia mai visto sia l'utilizzo di una tabella Database come memoria temporanea invece di utilizzare la memoria del computer.

Il dominio del problema è proprietario, il che mi impedisce di spiegarlo, ma non è necessario comprendere il problema di base. Questa era un'applicazione GUI scritta in Java con un database back-end. Doveva prendere determinati dati di input, manipolarli e quindi eseguire il commit dei dati elaborati nel database.

Il nostro progetto ha un algoritmo abbastanza complicato che salva i valori intermedi per l'elaborazione successiva. Invece di incapsulare gli oggetti temporanei in ... oggetti, è stata creata una tabella di database in questo modo "t_object". Ogni volta che un valore veniva calcolato, veniva aggiunto a questa tabella. Dopo che l'algoritmo ha terminato di funzionare, seleziona tutti i valori intermedi e li elabora in un unico oggetto Map grande. Al termine dell'elaborazione, i valori rimanenti che erano stati contrassegnati per essere salvati sarebbero stati aggiunti allo schema del database reale e le voci temporanee nella tabella "t_object" sarebbero state eliminate.

La tabella veniva anche utilizzata come un elenco univoco, i dati potevano esistere solo una volta. Questa potrebbe essere stata una caratteristica decente del progetto se avessimo implementato i Vincoli sulla tabella, ma alla fine abbiamo ripetuto l'intera tabella per vedere se i dati esistessero o meno. (No, non abbiamo nemmeno usato query che utilizzavano clausole where con CONTAINS)

Alcuni dei problemi che abbiamo riscontrato a causa di questo progetto sono stati il ​​debug specifico. L'applicazione è stata creata per pipeline di dati, quindi ci sarebbero più GUI che pre-elaborerebbero i dati prima che arrivassero a questo algoritmo. Il processo di debug consisteva nell'elaborare un caso di test e quindi mettere in pausa subito dopo aver completato la sezione precedente. Quindi interrogheremo il database per vedere quali dati erano contenuti in questa tabella.

Un altro problema che abbiamo scoperto è che i dati non venivano eliminati correttamente da questa tabella temporanea, il che avrebbe interferito con l'esecuzione in futuro. Abbiamo scoperto che ciò era dovuto al fatto che le eccezioni non venivano gestite correttamente e quindi l'applicazione non usciva correttamente e non cancellava i dati nella tabella di cui era in controllo.

Se avessimo utilizzato il design orientato agli oggetti di base e avessimo tenuto tutto in memoria, questi problemi di cui sopra non si sarebbero mai verificati. Innanzitutto, il debug sarebbe stato semplice in quanto potevamo facilmente impostare i punti di interruzione nell'applicazione e quindi ispezionare la memoria nello stack e nell'heap. In secondo luogo, all'uscita anomala dall'applicazione, la memoria java sarebbe stata ripulita naturalmente senza doversi preoccupare di eliminarla dal database.

NOTA: non sto dicendo che questo modello sia intrinsecamente negativo, ma in questo esempio l'ho trovato inutile quando i principi di base di OO sarebbero stati sufficienti.

Non sono sicuro di un nome per questo anti-pattern poiché questa è la prima volta che vedo qualcosa di simile fatto. Qualche buon nome a cui potete pensare per questo modello?


Non lo definirei un problema generale. Dipende da quanti dati temporanei vengono archiviati e da cosa viene fatto.
GrandmasterB,

Dovrei aggiungere altro al post, ma il problema principale era che invece di modificare oggetti e accedere ai dati dalla memoria, i dati venivano manipolati con il codice sql di hack. Ciò ha comportato una maggiore complessità e gravi problemi di prestazioni.
jluzwick,

2
Non menzioni il tuo dominio problematico, ma non sempre è una cosa negativa, mantenere lo stato altrove come questo potrebbe consentire ai processi di ridimensionarsi e introspettersi durante lunghi periodi di tempo, anche per aiutare il debug. L'accesso al DB ha causato problemi o preoccupazioni in termini di prestazioni?
Jé Queue,

Ho modificato l'articolo per riflettere meglio come è stato utilizzato nel nostro progetto, ma abbiamo riscontrato numerosi problemi durante il debug, in particolare perché abbiamo dovuto interrogare il database per vedere le variabili temporanee. Inoltre, abbiamo riscontrato grossi problemi relativi alle prestazioni a causa della costante interrogazione del database per i dati e del controllo della presenza di nuovi dati.
jluzwick,

0

Cart-Before-The-Horse - alias YouMightNeedIt

Per esempio:

  • Creazione di uno schema RDMB con concetti astratti, riferimenti incrociati - in sostanza un cubo di dati eccessivamente generalizzato ... E POI scrivere funzioni attorno a un modello universale .

Sarebbe il gemello malvagio di YAGNI, giusto?
Wayne Molina,

0

L'IMO anti-pattern peggiore che abbia mai visto è l'anti-pattern "Non abbiamo bisogno di alcun modello puzzolente": l'idea che i pattern di progettazione siano una perdita di tempo e che tu possa scrivere il codice più velocemente semplicemente facendolo scorrere e copiando / incollare secondo necessità.

La menzione d'onore va al codice che carica un oggetto dal database usando il vecchio stile VB6 di:

Foobar oFoo = new Foobar();
oFoo.FooID = 42;
if (oFoo.Load()) { 
    // do something with oFoo
}

in realtà non è un anti-schema, di per sé, ma mostra una mancanza di sfruttamento della corretta architettura e separazione delle preoccupazioni.

Inoltre, cose come questa:

// this name is misleading, we may not always want to stand in fire,
// we may want to stand in slime or voidzones or ice patches...
public Foobar StandInFire() { }

// why is this here???
public string BeatWithNerfBat(string whom) { }

// ????
public int GivePony(string to) { }
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.