L'accoppiamento con le stringhe "è più lento" rispetto ai metodi di classe?

18

Sto avviando un progetto di gruppo scolastico in Java, utilizzando Swing. È una semplice interfaccia grafica per l'app desktop Database.

Il professore ci ha dato il codice del progetto dell'anno scorso in modo da poter vedere come fa le cose. La mia impressione iniziale è che il codice è molto più complicato di quanto dovrebbe essere, ma immagino che i programmatori pensino spesso questo quando osservano il codice che non hanno semplicemente scritto.

Spero di trovare motivi per cui il suo sistema è buono o cattivo. (Ho chiesto al professore e ha detto che avrei visto più tardi perché è meglio, il che non mi soddisfa.)

In sostanza, per evitare qualsiasi accoppiamento tra i suoi oggetti persistibili, i modelli (business logic) e le viste, tutto è fatto con le stringhe. Gli oggetti persistibili che vengono archiviati nel database sono hashtable di stringhe e i modelli e le viste si "sottoscrivono" tra loro, fornendo chiavi di stringa per gli "eventi" ai quali si iscrivono.

Quando viene attivato un evento, la vista o il modello invia una stringa a tutti i suoi abbonati che decidono cosa fare per quell'evento. Ad esempio, in una delle viste, i metodi del listener di azioni (credo che questo semplicemente imposta il BicycleMakeField sull'oggetto persistibile):

    else if(evt.getSource() == bicycleMakeField)
    {
        myRegistry.updateSubscribers("BicycleMake", bicycleMakeField.getText());
    }

La chiamata alla fine arriva a questo metodo nel modello Vehicle:

public void stateChangeRequest(String key, Object value) {

            ... bunch of else ifs ...

    else
    if (key.equals("BicycleMake") == true)
    {
                ... do stuff ...

Il professore dice che questo modo di fare le cose è più estensibile e mantenibile rispetto a quando la vista chiama semplicemente un metodo su un oggetto di business logic. Dice che non esiste alcun accoppiamento tra viste e modelli perché non conoscono l'esistenza degli altri.

Penso che questo sia un tipo peggiore di accoppiamento, perché la vista e il modello devono usare le stesse stringhe per funzionare. Se rimuovi una vista o un modello o esegui un errore di battitura in una stringa, non ricevi errori di compilazione. Rende anche il codice molto più lungo di quanto penso debba essere.

Voglio discuterne con lui, ma usa la sua esperienza nel settore per contrastare qualsiasi argomento che io, lo studente inesperto, potrei fare. C'è qualche vantaggio nel suo approccio che mi manca?

Per chiarire, voglio confrontare l'approccio di cui sopra, con la visione ovviamente accoppiata al modello. Ad esempio, puoi passare l'oggetto del modello del veicolo alla vista, e quindi cambiare la "marca" del veicolo, fai questo:

vehicle.make = bicycleMakeField.getText();

Questo ridurrebbe le 15 linee di codice attualmente utilizzate SOLO per impostare la marca del veicolo in un posto, in una singola riga di codice leggibile. (E poiché questo tipo di operazione viene eseguita centinaia di volte in tutta l'app, penso che sarebbe una grande vittoria per la leggibilità e la sicurezza.)

Aggiorna

Il mio team leader e io abbiamo ristrutturato il framework nel modo in cui lo volevamo fare usando la tipizzazione statica, informato il professore e alla fine gli abbiamo dato una demo. È abbastanza generoso da permetterci di usare il nostro framework purché non gli chiediamo aiuto, e se possiamo mantenere il resto del nostro team in velocità - cosa che ci sembra giusta.

    
posta Philip 31.01.2012 - 15:52
fonte

8 risposte

32

L'approccio che il tuo professore propone è meglio descritto come scritto a caratteri stringenti ed è sbagliato su quasi tutti i livelli.

L'accoppiamento è meglio ridotto da inversione di dipendenza , che lo fa con una spedizione polimorfica, piuttosto che con un certo numero di casi .

    
risposta data 31.01.2012 - 16:11
fonte
19

Il tuo professore lo sta facendo male . Sta provando a completamente a disaccoppiare la vista e il modello forzando la comunicazione a viaggiare via stringa in entrambe le direzioni (la vista non dipende dal modello e il modello non dipende on view) , che sconfigge totalmente lo scopo della progettazione orientata agli oggetti e MVC. Sembra invece di scrivere classi e progettare un'architettura basata su OO, sta scrivendo moduli disparati che rispondono semplicemente ai messaggi testuali.

Per essere un po 'onesto con il professore, sta cercando di creare in Java ciò che sembra molto simile all'interfaccia .NET molto comune chiamata INotifyPropertyChanged , che notifica agli abbonati gli eventi di cambiamento tramite passare stringhe che specificano quale proprietà è cambiata. In .NET, questa interfaccia è principalmente utilizzata per comunicare da un modello di dati per visualizzare oggetti e elementi della GUI (binding).

Se fatto bene , con questo approccio puoi avere alcuni vantaggi¹:

  1. La vista dipende dal modello, ma il modello non deve dipendere dalla vista. Questo è appropriato Inversion of Control .
  2. Hai una sola posizione nel codice Visualizza che gestisce la risposta alle modifiche alle notifiche dal modello e solo un luogo per iscriversi / annullare la sottoscrizione, riducendo la probabilità di una perdita di memoria di riferimento sospesa.
  3. C'è molto meno overhead di codifica quando si desidera aggiungere o rimuovere un evento dall'editore dell'evento. In .NET esiste un meccanismo di pubblicazione / sottoscrizione integrato denominato events , ma in Java è necessario creare classi o oggetti e scrivere codice di sottoscrizione / annullamento dell'iscrizione per ciascun evento.
  4. Il più grande problema con questo approccio è che il codice di sottoscrizione può non essere sincronizzato con il codice di pubblicazione. Tuttavia, questo è anche un vantaggio in alcuni ambienti di sviluppo. Se un nuovo evento è stato sviluppato nel modello e la vista non è ancora stata aggiornata per iscriverti, allora la vista continuerà a funzionare. Allo stesso modo, se un evento viene rimosso, hai un codice morto, ma potrebbe comunque essere compilato ed eseguito.
  5. In .NET, almeno, Reflection è usato molto efficacemente qui per chiamare automaticamente getter e setter a seconda della stringa che è stata passata al sottoscrittore.

Quindi, in breve (e per rispondere alla tua domanda), può essere fatto, ma l'approccio che il tuo professore sta prendendo è sbagliato per non permettere almeno una dipendenza unidirezionale tra la vista e il modello. Non è solo pratico, eccessivamente accademico e non è un buon esempio di come utilizzare gli strumenti a portata di mano.

¹ Ricerca Google per INotifyPropertyChanged per trovare un milione di modi persone trovano a cercare di evitare l'uso di stringa magica quando lo implementa.

    
risposta data 31.01.2012 - 17:10
fonte
11

enum s (o public static final String s) dovrebbe essere usato al posto delle stringhe magiche.

Il tuo professore non capisce OO . Ad esempio, avendo una classe Veicolo concreta?
E per far sì che questa classe comprenda la marca di una bicicletta?

Il veicolo dovrebbe essere astratto e dovrebbe esserci una sottoclasse concreta chiamata Bike. Giocavo secondo le sue regole per ottenere un buon voto e poi dimenticare il suo insegnamento.

    
risposta data 31.01.2012 - 16:06
fonte
7

Penso che tu abbia un buon punto, le stringhe magiche sono cattive. Qualcuno nel mio team ha dovuto eseguire il debug di un problema causato da stringhe magiche un paio di settimane fa (ma era nel loro codice che hanno scritto così ho tenuto la bocca chiusa). Ed è successo ... nell'industria !!

Il vantaggio del suo approccio è che potrebbe essere più veloce da codificare, specialmente per piccoli progetti dimostrativi. Gli svantaggi sono che è incline a errori di battitura e più spesso la stringa viene utilizzata più possibilità un codice di errore avvita le cose. E se vuoi cambiare una stringa magica, il refactoring delle stringhe è più difficile del refactoring delle variabili dato che gli strumenti di refactoring potrebbero anche toccare stringhe che contengono la stringa magica (come sottostringa) ma sono non loro stessi la stringa magica e dovrebbero non essere toccati. Inoltre, potresti dover tenere un dizionario o un indice di stringhe magiche in modo che i nuovi sviluppatori non inizino a inventare nuove stringhe magiche per lo stesso scopo e non perderanno tempo a cercare le stringhe magiche esistenti.

In questo contesto, sembra che un Enum potrebbe essere migliore delle stringhe magiche o anche delle costanti di stringa globali. Inoltre, gli IDE ti forniscono spesso una sorta di code-assist (auto-completamento, refactoring, find-all-references, ecc ...) quando usi stringhe o enumerazioni costanti. Un IDE non offrirà molto aiuto se usi le stringhe magiche.

    
risposta data 31.01.2012 - 15:59
fonte
6

L'uso di "esperienza del settore" è un argomento povero. Il tuo professore ha bisogno di un argomento migliore di quello: -).

Come altri hanno affermato, l'uso delle stringhe magiche è sicuramente disapprovato, usando le enumerazioni considerate molto più sicure. Un enum ha significato e ambito , le stringhe magiche no. A parte questo, il modo in cui il tuo professore disaccoppia le preoccupazioni è considerato una tecnica più vecchia e più fragile di quella usata oggi.

Vedo che @backtodos ha trattato questo mentre stavo rispondendo a questo, quindi aggiungerò semplicemente la mia opinione che usando Inversion of Control (di cui Dependency Injection è una forma, ti imbatterai in tutto il framework Spring nell'industria fino a lungo ...) è considerato un modo più moderno per gestire questo tipo di disaccoppiamento.

    
risposta data 31.01.2012 - 16:16
fonte
4

Siamo abbastanza chiari; c'è inevitabilmente qualche accoppiamento in là. Il fatto che ci sia un argomento così abbonabile è un punto di accoppiamento, così come la natura del resto del messaggio (come "singola stringa"). Detto questo, la domanda diventa quindi se l'accoppiamento è maggiore quando viene eseguito tramite stringhe o tipi. La risposta a questo dipende dal fatto che tu sia preoccupato per l'accoppiamento distribuito nel tempo o nello spazio: se i messaggi verranno conservati in un file prima di essere letti in un secondo momento, o saranno inviati a un altro processo (specialmente se non è scritto nella stessa lingua), usare le stringhe può rendere le cose molto più semplici. Lo svantaggio è che non esiste un controllo di tipo; gli errori non verranno rilevati fino al runtime (e talvolta nemmeno in quel momento).

Una strategia di mitigazione / compromissione per Java può essere quella di utilizzare un'interfaccia tipizzata, ma dove l'interfaccia tipizzata si trova in un pacchetto separato. Dovrebbe comprendere solo Java interface s e tipi di valori di base (ad es. Enumerazioni, eccezioni). Ci dovrebbero essere implementazioni no delle interfacce in quel pacchetto. Quindi tutti si attengono a ciò e, se è necessario trasmettere i messaggi in modo complesso, una particolare implementazione di un delegato Java può utilizzare le stringhe magiche secondo necessità. Inoltre, rende molto più facile la migrazione del codice in un ambiente più professionale come JEE o OSGi, e assiste molto poco le cose come il test ...

    
risposta data 31.01.2012 - 16:13
fonte
4

Ciò che il tuo professore propone è l'uso di "stringhe magiche". Mentre fa "coppia liberamente" le classi tra di loro, consentendo modifiche più facili, è un anti-pattern; le stringhe dovrebbero molto, MOLTO raramente essere utilizzate per contenere o controllare le istruzioni del codice, e quando deve assolutamente accadere, il valore delle stringhe che si stanno utilizzando per controllare la logica dovrebbe essere definito centralmente e costantemente in modo che ci sia UN'autorità sul valore atteso di qualsiasi stringa particolare.

Il motivo per cui è molto semplice; le stringhe non vengono controllate dal compilatore per la sintassi o la coerenza con altri valori (nella migliore delle ipotesi vengono verificate le forme corrette per i caratteri di escape e altre formattazioni specifiche della lingua all'interno della stringa). Puoi mettere tutto ciò che vuoi in una stringa. Come tale, è possibile ottenere un algoritmo / programma irrimediabilmente rotto da compilare, e non è fino a quando non si verifica che si verifichi un errore. Considera il seguente codice (C #):

private Dictionary<string, Func<string>> methods;

private void InitDictionary() //called elsewhere
{
   methods = new Dictionary<string, Func<string>> 
      {{"Method1", ()=>doSomething()},
       {"MEthod2", ()=>doSomethingElse()}} //oops, fat-fingered it
}

public string RunMethod(string methodName)
{
   //very naive, but even a check for the key would generate a runtime error, 
   //not a compile-time one.
   return methods[methodName](); 
}

...

//this is what we thought we entered back in InitDictionary for this method...
var result = RunMethod("Method2"); //error; no such key

... tutto questo codice viene compilato, prima prova, ma l'errore è ovvio con una seconda occhiata. Esistono numerosi esempi di questo tipo di programmazione, e sono tutti soggetti a questo tipo di errore, ANCHE SE si definiscono stringhe come costanti (poiché le costanti in .NET sono scritte sul manifest di ogni libreria in cui vengono utilizzate, che deve quindi tutti vengono ricompilati quando il valore definito viene modificato in modo che il valore cambi "globalmente"). Poiché l'errore non viene catturato in fase di compilazione, deve essere catturato durante i test in fase di esecuzione, e questo è garantito solo con una copertura del 100% del codice nei test unitari con un esercizio di codice adeguato, che di solito si trova solo nel fail-safe più assoluto , sistemi in tempo reale.

    
risposta data 31.01.2012 - 18:14
fonte
2

In realtà, quelle classi sono ancora strettamente accoppiate. Tranne che ora sono strettamente accoppiati in modo tale che il compilatore non può dirti quando le cose si sono rotte!

Se qualcuno cambia "BicycleMake" in "BicyclesMake", allora nessuno scoprirà che le cose sono rotte fino al runtime.

Gli errori di runtime sono molto più costosi da correggere rispetto agli errori di compilazione del tempo - questo è solo un tipo di male.

    
risposta data 29.02.2012 - 22:19
fonte

Leggi altre domande sui tag